xref: /aosp_15_r20/external/skia/gm/image_pict.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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