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