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