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/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkImageGenerator.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkMatrix.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPicture.h"
19 #include "include/core/SkPictureRecorder.h"
20 #include "include/core/SkPoint.h"
21 #include "include/core/SkRect.h"
22 #include "include/core/SkRefCnt.h"
23 #include "include/core/SkScalar.h"
24 #include "include/core/SkSize.h"
25 #include "include/core/SkString.h"
26 #include "include/core/SkSurface.h"
27 #include "include/core/SkTypes.h"
28 #include "include/gpu/ganesh/GrDirectContext.h"
29 #include "include/gpu/ganesh/GrRecordingContext.h"
30 #include "include/gpu/ganesh/GrTypes.h"
31 #include "include/gpu/ganesh/GrExternalTextureGenerator.h"
32 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
33 #include "include/private/gpu/ganesh/GrTextureGenerator.h"
34 #include "include/private/gpu/ganesh/GrTypesPriv.h"
35 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
36 #include "src/gpu/ganesh/GrSamplerState.h"
37 #include "src/gpu/ganesh/GrTextureProxy.h"
38 #include "src/gpu/ganesh/SurfaceContext.h"
39 #include "src/gpu/ganesh/image/GrImageUtils.h"
40 #include "src/gpu/ganesh/image/SkImage_Ganesh.h"
41 #include "src/image/SkImageGeneratorPriv.h"
42 #include "src/image/SkImage_Base.h"
43
44 #include <memory>
45 #include <utility>
46
47 #if defined(SK_GRAPHITE)
48 #include "include/gpu/graphite/Surface.h"
49 #endif
50
51 class GrRecordingContext;
52
draw_something(SkCanvas * canvas,const SkRect & bounds)53 static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
54 SkPaint paint;
55 paint.setAntiAlias(true);
56 paint.setColor(SK_ColorRED);
57 paint.setStyle(SkPaint::kStroke_Style);
58 paint.setStrokeWidth(10);
59 canvas->drawRect(bounds, paint);
60 paint.setStyle(SkPaint::kFill_Style);
61 paint.setColor(SK_ColorBLUE);
62 canvas->drawOval(bounds, paint);
63 }
64
65 /*
66 * Exercise drawing pictures inside an image, showing that the image version is pixelated
67 * (correctly) when it is inside an image.
68 */
69 class ImagePictGM : public skiagm::GM {
70 sk_sp<SkPicture> fPicture;
71 sk_sp<SkImage> fImage0;
72 sk_sp<SkImage> fImage1;
73 public:
ImagePictGM()74 ImagePictGM() {}
75
76 protected:
getName() const77 SkString getName() const override { return SkString("image-picture"); }
78
getISize()79 SkISize getISize() override { return SkISize::Make(850, 450); }
80
onOnceBeforeDraw()81 void onOnceBeforeDraw() override {
82 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
83 SkPictureRecorder recorder;
84 draw_something(recorder.beginRecording(bounds), bounds);
85 fPicture = recorder.finishRecordingAsPicture();
86
87 // extract enough just for the oval.
88 const SkISize size = SkISize::Make(100, 100);
89 auto srgbColorSpace = SkColorSpace::MakeSRGB();
90
91 SkMatrix matrix;
92 matrix.setTranslate(-100, -100);
93 fImage0 = SkImages::DeferredFromPicture(
94 fPicture, size, &matrix, nullptr, SkImages::BitDepth::kU8, srgbColorSpace);
95 matrix.postTranslate(-50, -50);
96 matrix.postRotate(45);
97 matrix.postTranslate(50, 50);
98 fImage1 = SkImages::DeferredFromPicture(
99 fPicture, size, &matrix, nullptr, SkImages::BitDepth::kU8, srgbColorSpace);
100 }
101
drawSet(SkCanvas * canvas) const102 void drawSet(SkCanvas* canvas) const {
103 SkMatrix matrix = SkMatrix::Translate(-100, -100);
104 canvas->drawPicture(fPicture, &matrix, nullptr);
105 canvas->drawImage(fImage0.get(), 150, 0);
106 canvas->drawImage(fImage1.get(), 300, 0);
107 }
108
onDraw(SkCanvas * canvas)109 void onDraw(SkCanvas* canvas) override {
110 canvas->translate(20, 20);
111
112 this->drawSet(canvas);
113
114 canvas->save();
115 canvas->translate(0, 130);
116 canvas->scale(0.25f, 0.25f);
117 this->drawSet(canvas);
118 canvas->restore();
119
120 canvas->save();
121 canvas->translate(0, 200);
122 canvas->scale(2, 2);
123 this->drawSet(canvas);
124 canvas->restore();
125 }
126
127 private:
128 using INHERITED = skiagm::GM;
129 };
DEF_GM(return new ImagePictGM;)130 DEF_GM( return new ImagePictGM; )
131
132 ///////////////////////////////////////////////////////////////////////////////////////////////////
133
134 static std::unique_ptr<SkImageGenerator> make_pic_generator(SkCanvas*,
135 sk_sp<SkPicture> pic) {
136 SkMatrix matrix;
137 matrix.setTranslate(-100, -100);
138 return SkImageGenerators::MakeFromPicture({100, 100},
139 std::move(pic),
140 &matrix,
141 nullptr,
142 SkImages::BitDepth::kU8,
143 SkColorSpace::MakeSRGB());
144 }
145
146 class RasterGenerator : public SkImageGenerator {
147 public:
RasterGenerator(const SkBitmap & bm)148 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm)
149 {}
150
151 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options &)152 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
153 const Options&) override {
154 SkASSERT(fBM.width() == info.width());
155 SkASSERT(fBM.height() == info.height());
156 return fBM.readPixels(info, pixels, rowBytes, 0, 0);
157 }
158 private:
159 SkBitmap fBM;
160 };
make_ras_generator(SkCanvas *,sk_sp<SkPicture> pic)161 static std::unique_ptr<SkImageGenerator> make_ras_generator(SkCanvas*,
162 sk_sp<SkPicture> pic) {
163 SkBitmap bm;
164 bm.allocN32Pixels(100, 100);
165 SkCanvas canvas(bm);
166 canvas.clear(0);
167 canvas.translate(-100, -100);
168 canvas.drawPicture(pic);
169 return std::make_unique<RasterGenerator>(bm);
170 }
171
172 class TextureGenerator : public GrTextureGenerator {
173 public:
TextureGenerator(SkCanvas * canvas,const SkImageInfo & info,sk_sp<SkPicture> pic)174 TextureGenerator(SkCanvas* canvas, const SkImageInfo& info, sk_sp<SkPicture> pic)
175 : GrTextureGenerator(info) {
176
177 fRContext = sk_ref_sp(canvas->recordingContext());
178
179 sk_sp<SkSurface> surface;
180
181 if (fRContext) {
182 surface = SkSurfaces::RenderTarget(fRContext.get(),
183 skgpu::Budgeted::kYes,
184 info,
185 0,
186 kTopLeft_GrSurfaceOrigin,
187 nullptr);
188 }
189 #if defined(SK_GRAPHITE)
190 if (skgpu::graphite::Recorder* recorder = canvas->recorder()) {
191 surface = SkSurfaces::RenderTarget(recorder, info);
192 }
193 #endif
194
195 if (surface) {
196 surface->getCanvas()->clear(0);
197 surface->getCanvas()->translate(-100, -100);
198 surface->getCanvas()->drawPicture(pic);
199 fImage = surface->makeImageSnapshot();
200 }
201 }
202 protected:
onGenerateTexture(GrRecordingContext * rContext,const SkImageInfo & info,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy policy)203 GrSurfaceProxyView onGenerateTexture(GrRecordingContext* rContext,
204 const SkImageInfo& info,
205 skgpu::Mipmapped mipmapped,
206 GrImageTexGenPolicy policy) override {
207 SkASSERT(rContext);
208 SkASSERT(rContext->priv().matches(fRContext.get()));
209
210 auto [view, _] = skgpu::ganesh::AsView(rContext, fImage, skgpu::Mipmapped::kNo);
211 if (!view) {
212 return {};
213 }
214
215 SkASSERT_RELEASE(info.dimensions() == view.proxy()->dimensions());
216
217 if (policy == GrImageTexGenPolicy::kDraw) {
218 return view;
219 }
220 auto budgeted = policy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted
221 ? skgpu::Budgeted::kNo
222 : skgpu::Budgeted::kYes;
223 return GrSurfaceProxyView::Copy(
224 fRContext.get(),
225 view,
226 mipmapped,
227 SkIRect::MakeWH(info.width(), info.height()),
228 SkBackingFit::kExact,
229 budgeted,
230 /*label=*/"SurfaceProxyView_GenerateTexture");
231 }
232
233 private:
234 sk_sp<GrRecordingContext> fRContext;
235 sk_sp<SkImage> fImage;
236 };
237
make_tex_generator(SkCanvas * canvas,sk_sp<SkPicture> pic)238 static std::unique_ptr<SkImageGenerator> make_tex_generator(SkCanvas* canvas,
239 sk_sp<SkPicture> pic) {
240 auto dContext = GrAsDirectContext(canvas->recordingContext());
241 if (!dContext && !canvas->recorder()) {
242 return nullptr;
243 }
244
245 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
246
247 return std::make_unique<TextureGenerator>(canvas, info, pic);
248 }
249
250 class ImageCacheratorGM : public skiagm::GM {
251 typedef std::unique_ptr<SkImageGenerator> (*FactoryFunc)(SkCanvas*, sk_sp<SkPicture>);
252
253 SkString fName;
254 FactoryFunc fFactory;
255 sk_sp<SkPicture> fPicture;
256 sk_sp<SkImage> fImage;
257 sk_sp<SkImage> fImageSubset;
258 bool fUseTexture;
259
260 public:
ImageCacheratorGM(const char suffix[],FactoryFunc factory,bool useTexture)261 ImageCacheratorGM(const char suffix[], FactoryFunc factory, bool useTexture) :
262 fFactory(factory), fUseTexture(useTexture) {
263 fName.printf("image-cacherator-from-%s", suffix);
264 }
265
266 protected:
getName() const267 SkString getName() const override { return fName; }
268
getISize()269 SkISize getISize() override { return SkISize::Make(960, 450); }
270
onOnceBeforeDraw()271 void onOnceBeforeDraw() override {
272 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
273 SkPictureRecorder recorder;
274 draw_something(recorder.beginRecording(bounds), bounds);
275 fPicture = recorder.finishRecordingAsPicture();
276 }
277
makeCaches(SkCanvas * canvas)278 bool makeCaches(SkCanvas* canvas) {
279 auto dContext = GrAsDirectContext(canvas->recordingContext());
280
281 {
282 auto gen = fFactory(canvas, fPicture);
283 if (!gen) {
284 return false;
285 }
286 if (fUseTexture) {
287 auto textureGen = std::unique_ptr<GrTextureGenerator>(
288 static_cast<GrTextureGenerator*>(gen.release()));
289 fImage = SkImages::DeferredFromTextureGenerator(std::move(textureGen));
290 } else {
291 fImage = SkImages::DeferredFromGenerator(std::move(gen));
292 }
293 if (!fImage) {
294 return false;
295 }
296 SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
297 }
298
299 {
300 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
301
302 // We re-create the generator here on the off chance that making a subset from
303 // 'fImage' might perturb its state.
304 auto gen = fFactory(canvas, fPicture);
305 if (!gen) {
306 return false;
307 }
308
309 if (dContext) {
310 if (fUseTexture) {
311 auto textureGen = std::unique_ptr<GrTextureGenerator>(
312 static_cast<GrTextureGenerator*>(gen.release()));
313 fImageSubset = SkImages::DeferredFromTextureGenerator(std::move(textureGen))
314 ->makeSubset(dContext, subset);
315 } else {
316 fImageSubset = SkImages::DeferredFromGenerator(std::move(gen))
317 ->makeSubset(dContext, subset);
318 }
319 } else {
320 #if defined(SK_GRAPHITE)
321 auto recorder = canvas->recorder();
322 fImageSubset = SkImages::DeferredFromGenerator(std::move(gen))
323 ->makeSubset(recorder, subset, {});
324 #endif
325 }
326 if (!fImageSubset) {
327 return false;
328 }
329 SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
330 }
331
332 return true;
333 }
334
draw_placeholder(SkCanvas * canvas,SkScalar x,SkScalar y,int w,int h)335 static void draw_placeholder(SkCanvas* canvas, SkScalar x, SkScalar y, int w, int h) {
336 SkPaint paint;
337 paint.setStyle(SkPaint::kStroke_Style);
338 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(w), SkIntToScalar(h));
339 canvas->drawRect(r, paint);
340 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
341 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
342 }
343
draw_as_bitmap(GrDirectContext * dContext,SkCanvas * canvas,SkImage * image,SkScalar x,SkScalar y)344 static void draw_as_bitmap(GrDirectContext* dContext, SkCanvas* canvas, SkImage* image,
345 SkScalar x, SkScalar y) {
346 SkBitmap bitmap;
347 if (as_IB(image)->getROPixels(dContext, &bitmap)) {
348 canvas->drawImage(bitmap.asImage(), x, y);
349 } else {
350 draw_placeholder(canvas, x, y, image->width(), image->height());
351 }
352 }
353
draw_as_tex(SkCanvas * canvas,SkImage * image,SkScalar x,SkScalar y)354 static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
355 if (as_IB(image)->isGaneshBacked()) {
356 // The gpu-backed images are drawn in this manner bc the generator backed images
357 // aren't considered texture-backed
358 auto [view, ct] =
359 skgpu::ganesh::AsView(canvas->recordingContext(), image, skgpu::Mipmapped::kNo);
360 if (!view) {
361 // show placeholder if we have no texture
362 draw_placeholder(canvas, x, y, image->width(), image->height());
363 return;
364 }
365 SkColorInfo colorInfo(GrColorTypeToSkColorType(ct),
366 image->alphaType(),
367 image->refColorSpace());
368 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
369 sk_sp<SkImage> texImage(new SkImage_Ganesh(sk_ref_sp(canvas->recordingContext()),
370 image->uniqueID(),
371 std::move(view),
372 std::move(colorInfo)));
373 canvas->drawImage(texImage.get(), x, y);
374 } else {
375 canvas->drawImage(image, x, y);
376 }
377 }
378
drawRow(GrDirectContext * dContext,SkCanvas * canvas,float scale) const379 void drawRow(GrDirectContext* dContext, SkCanvas* canvas, float scale) const {
380 canvas->scale(scale, scale);
381
382 SkMatrix matrix = SkMatrix::Translate(-100, -100);
383 canvas->drawPicture(fPicture, &matrix, nullptr);
384
385 // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
386 // way we also can force the generateTexture call.
387
388 draw_as_tex(canvas, fImage.get(), 150, 0);
389 draw_as_tex(canvas, fImageSubset.get(), 150+101, 0);
390
391 draw_as_bitmap(dContext, canvas, fImage.get(), 310, 0);
392 draw_as_bitmap(dContext, canvas, fImageSubset.get(), 310+101, 0);
393 }
394
onDraw(SkCanvas * canvas,SkString * errorMsg)395 DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
396 auto dContext = GrAsDirectContext(canvas->recordingContext());
397 if (!this->makeCaches(canvas)) {
398 errorMsg->printf("Could not create cached images");
399 return DrawResult::kSkip;
400 }
401
402 canvas->save();
403 canvas->translate(20, 20);
404 this->drawRow(dContext, canvas, 1.0);
405 canvas->restore();
406
407 canvas->save();
408 canvas->translate(20, 150);
409 this->drawRow(dContext, canvas, 0.25f);
410 canvas->restore();
411
412 canvas->save();
413 canvas->translate(20, 220);
414 this->drawRow(dContext, canvas, 2.0f);
415 canvas->restore();
416
417 return DrawResult::kOk;
418 }
419
420 private:
421 using INHERITED = skiagm::GM;
422 };
423
424 DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator, false); )
425 DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator, false); )
426 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator, true); )
427