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.h"
9#include "SkCanvas.h"
10#include "SkImage.h"
11#include "SkImageGenerator.h"
12#include "SkImage_Base.h"
13#include "SkMakeUnique.h"
14#include "SkPictureRecorder.h"
15#include "SkSurface.h"
16
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
19#include "GrContextPriv.h"
20#include "GrSurfaceContext.h"
21#include "GrTextureProxy.h"
22#include "../src/image/SkImage_Gpu.h"
23#endif
24
25static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
26    SkPaint paint;
27    paint.setAntiAlias(true);
28    paint.setColor(SK_ColorRED);
29    paint.setStyle(SkPaint::kStroke_Style);
30    paint.setStrokeWidth(10);
31    canvas->drawRect(bounds, paint);
32    paint.setStyle(SkPaint::kFill_Style);
33    paint.setColor(SK_ColorBLUE);
34    canvas->drawOval(bounds, paint);
35}
36
37/*
38 *  Exercise drawing pictures inside an image, showing that the image version is pixelated
39 *  (correctly) when it is inside an image.
40 */
41class ImagePictGM : public skiagm::GM {
42    sk_sp<SkPicture> fPicture;
43    sk_sp<SkImage>   fImage0;
44    sk_sp<SkImage>   fImage1;
45public:
46    ImagePictGM() {}
47
48protected:
49    SkString onShortName() override {
50        return SkString("image-picture");
51    }
52
53    SkISize onISize() override {
54        return SkISize::Make(850, 450);
55    }
56
57    void onOnceBeforeDraw() override {
58        const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
59        SkPictureRecorder recorder;
60        draw_something(recorder.beginRecording(bounds), bounds);
61        fPicture = recorder.finishRecordingAsPicture();
62
63        // extract enough just for the oval.
64        const SkISize size = SkISize::Make(100, 100);
65        auto srgbColorSpace = SkColorSpace::MakeSRGB();
66
67        SkMatrix matrix;
68        matrix.setTranslate(-100, -100);
69        fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
70                                           SkImage::BitDepth::kU8, srgbColorSpace);
71        matrix.postTranslate(-50, -50);
72        matrix.postRotate(45);
73        matrix.postTranslate(50, 50);
74        fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
75                                           SkImage::BitDepth::kU8, srgbColorSpace);
76    }
77
78    void drawSet(SkCanvas* canvas) const {
79        SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
80        canvas->drawPicture(fPicture, &matrix, nullptr);
81        canvas->drawImage(fImage0.get(), 150, 0);
82        canvas->drawImage(fImage1.get(), 300, 0);
83    }
84
85    void onDraw(SkCanvas* canvas) override {
86        canvas->translate(20, 20);
87
88        this->drawSet(canvas);
89
90        canvas->save();
91        canvas->translate(0, 130);
92        canvas->scale(0.25f, 0.25f);
93        this->drawSet(canvas);
94        canvas->restore();
95
96        canvas->save();
97        canvas->translate(0, 200);
98        canvas->scale(2, 2);
99        this->drawSet(canvas);
100        canvas->restore();
101    }
102
103private:
104    typedef skiagm::GM INHERITED;
105};
106DEF_GM( return new ImagePictGM; )
107
108///////////////////////////////////////////////////////////////////////////////////////////////////
109
110static std::unique_ptr<SkImageGenerator> make_pic_generator(GrContext*, sk_sp<SkPicture> pic) {
111    SkMatrix matrix;
112    matrix.setTranslate(-100, -100);
113    return SkImageGenerator::MakeFromPicture({ 100, 100 }, std::move(pic), &matrix, nullptr,
114                                            SkImage::BitDepth::kU8,
115                                            SkColorSpace::MakeSRGB());
116}
117
118class RasterGenerator : public SkImageGenerator {
119public:
120    RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm)
121    {}
122
123protected:
124    bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
125                     const Options&) override {
126        SkASSERT(fBM.width() == info.width());
127        SkASSERT(fBM.height() == info.height());
128        return fBM.readPixels(info, pixels, rowBytes, 0, 0);
129    }
130private:
131    SkBitmap fBM;
132};
133static std::unique_ptr<SkImageGenerator> make_ras_generator(GrContext*, sk_sp<SkPicture> pic) {
134    SkBitmap bm;
135    bm.allocN32Pixels(100, 100);
136    SkCanvas canvas(bm);
137    canvas.clear(0);
138    canvas.translate(-100, -100);
139    canvas.drawPicture(pic);
140    return skstd::make_unique<RasterGenerator>(bm);
141}
142
143class EmptyGenerator : public SkImageGenerator {
144public:
145    EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
146};
147
148#if SK_SUPPORT_GPU
149class TextureGenerator : public SkImageGenerator {
150public:
151    TextureGenerator(GrContext* ctx, const SkImageInfo& info, sk_sp<SkPicture> pic)
152        : SkImageGenerator(info)
153        , fCtx(SkRef(ctx)) {
154
155        sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, info, 0,
156                                                             kTopLeft_GrSurfaceOrigin, nullptr));
157        if (surface) {
158            surface->getCanvas()->clear(0);
159            surface->getCanvas()->translate(-100, -100);
160            surface->getCanvas()->drawPicture(pic);
161            sk_sp<SkImage> image(surface->makeImageSnapshot());
162            fProxy = as_IB(image)->asTextureProxyRef();
163        }
164    }
165protected:
166    sk_sp<GrTextureProxy> onGenerateTexture(GrContext* ctx, const SkImageInfo& info,
167                                            const SkIPoint& origin,
168                                            SkTransferFunctionBehavior,
169                                            bool willBeMipped) override {
170        SkASSERT(ctx);
171        SkASSERT(ctx == fCtx.get());
172
173        if (!fProxy) {
174            return nullptr;
175        }
176
177        if (origin.fX == 0 && origin.fY == 0 &&
178            info.width() == fProxy->width() && info.height() == fProxy->height()) {
179            return fProxy;
180        }
181
182        // need to copy the subset into a new texture
183        GrSurfaceDesc desc;
184        desc.fOrigin = fProxy->origin();
185        desc.fWidth = info.width();
186        desc.fHeight = info.height();
187        desc.fConfig = fProxy->config();
188
189        GrMipMapped mipMapped = willBeMipped ? GrMipMapped::kYes : GrMipMapped::kNo;
190
191        sk_sp<GrSurfaceContext> dstContext(fCtx->contextPriv().makeDeferredSurfaceContext(
192                                                                            desc,
193                                                                            mipMapped,
194                                                                            SkBackingFit::kExact,
195                                                                            SkBudgeted::kYes));
196        if (!dstContext) {
197            return nullptr;
198        }
199
200        if (!dstContext->copy(
201                            fProxy.get(),
202                            SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
203                            SkIPoint::Make(0, 0))) {
204            return nullptr;
205        }
206
207        return dstContext->asTextureProxyRef();
208    }
209
210private:
211    sk_sp<GrContext>      fCtx;
212    sk_sp<GrTextureProxy> fProxy;
213};
214
215static std::unique_ptr<SkImageGenerator> make_tex_generator(GrContext* ctx, sk_sp<SkPicture> pic) {
216    const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
217
218    if (!ctx) {
219        return skstd::make_unique<EmptyGenerator>(info);
220    }
221    return skstd::make_unique<TextureGenerator>(ctx, info, pic);
222}
223#endif
224
225class ImageCacheratorGM : public skiagm::GM {
226    SkString                         fName;
227    std::unique_ptr<SkImageGenerator> (*fFactory)(GrContext*, sk_sp<SkPicture>);
228    sk_sp<SkPicture>                 fPicture;
229    sk_sp<SkImage>                   fImage;
230    sk_sp<SkImage>                   fImageSubset;
231
232public:
233    ImageCacheratorGM(const char suffix[],
234                      std::unique_ptr<SkImageGenerator> (*factory)(GrContext*, sk_sp<SkPicture>))
235        : fFactory(factory)
236    {
237        fName.printf("image-cacherator-from-%s", suffix);
238    }
239
240protected:
241    SkString onShortName() override {
242        return fName;
243    }
244
245    SkISize onISize() override {
246        return SkISize::Make(960, 450);
247    }
248
249    void onOnceBeforeDraw() override {
250        const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
251        SkPictureRecorder recorder;
252        draw_something(recorder.beginRecording(bounds), bounds);
253        fPicture = recorder.finishRecordingAsPicture();
254    }
255
256    void makeCaches(GrContext* ctx) {
257        auto gen = fFactory(ctx, fPicture);
258        fImage = SkImage::MakeFromGenerator(std::move(gen));
259
260        const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
261
262        gen = fFactory(ctx, fPicture);
263        fImageSubset = SkImage::MakeFromGenerator(std::move(gen), &subset);
264
265        SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
266        SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
267    }
268
269    static void draw_as_bitmap(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
270        SkBitmap bitmap;
271        as_IB(image)->getROPixels(&bitmap, canvas->imageInfo().colorSpace());
272        canvas->drawBitmap(bitmap, x, y);
273    }
274
275    static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
276#if SK_SUPPORT_GPU
277        sk_sp<SkColorSpace> texColorSpace;
278        sk_sp<GrTextureProxy> proxy(as_IB(image)->asTextureProxyRef(
279                canvas->getGrContext(), GrSamplerState::ClampBilerp(),
280                canvas->imageInfo().colorSpace(), &texColorSpace, nullptr));
281        if (!proxy) {
282            // show placeholder if we have no texture
283            SkPaint paint;
284            paint.setStyle(SkPaint::kStroke_Style);
285            SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(image->width()),
286                                        SkIntToScalar(image->width()));
287            canvas->drawRect(r, paint);
288            canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
289            canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
290            return;
291        }
292
293        // No API to draw a GrTexture directly, so we cheat and create a private image subclass
294        sk_sp<SkImage> texImage(new SkImage_Gpu(canvas->getGrContext(), image->uniqueID(),
295                                                kPremul_SkAlphaType, std::move(proxy),
296                                                std::move(texColorSpace), SkBudgeted::kNo));
297        canvas->drawImage(texImage.get(), x, y);
298#endif
299    }
300
301    void drawSet(SkCanvas* canvas) const {
302        SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
303        canvas->drawPicture(fPicture, &matrix, nullptr);
304
305        // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
306        // way we also can force the generateTexture call.
307
308        draw_as_tex(canvas, fImage.get(), 310, 0);
309        draw_as_tex(canvas, fImageSubset.get(), 310+101, 0);
310
311        draw_as_bitmap(canvas, fImage.get(), 150, 0);
312        draw_as_bitmap(canvas, fImageSubset.get(), 150+101, 0);
313    }
314
315    void onDraw(SkCanvas* canvas) override {
316        this->makeCaches(canvas->getGrContext());
317
318        canvas->translate(20, 20);
319
320        this->drawSet(canvas);
321
322        canvas->save();
323        canvas->translate(0, 130);
324        canvas->scale(0.25f, 0.25f);
325        this->drawSet(canvas);
326        canvas->restore();
327
328        canvas->save();
329        canvas->translate(0, 200);
330        canvas->scale(2, 2);
331        this->drawSet(canvas);
332        canvas->restore();
333    }
334
335private:
336    typedef skiagm::GM INHERITED;
337};
338DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
339DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
340#if SK_SUPPORT_GPU
341    DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
342#endif
343