xref: /aosp_15_r20/external/skia/gm/image_shader.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2015 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkEncodedImageFormat.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTileMode.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkPngEncoder.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/GpuTypes.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/SkSurfaceGanesh.h"
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
34*c8dee2aaSAndroid Build Coastguard Worker 
draw_something(SkCanvas * canvas,const SkRect & bounds)35*c8dee2aaSAndroid Build Coastguard Worker static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
36*c8dee2aaSAndroid Build Coastguard Worker     SkPaint paint;
37*c8dee2aaSAndroid Build Coastguard Worker     paint.setAntiAlias(true);
38*c8dee2aaSAndroid Build Coastguard Worker     paint.setColor(SK_ColorRED);
39*c8dee2aaSAndroid Build Coastguard Worker     paint.setStyle(SkPaint::kStroke_Style);
40*c8dee2aaSAndroid Build Coastguard Worker     paint.setStrokeWidth(10);
41*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawRect(bounds, paint);
42*c8dee2aaSAndroid Build Coastguard Worker     paint.setStyle(SkPaint::kFill_Style);
43*c8dee2aaSAndroid Build Coastguard Worker     paint.setColor(SK_ColorBLUE);
44*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawOval(bounds, paint);
45*c8dee2aaSAndroid Build Coastguard Worker }
46*c8dee2aaSAndroid Build Coastguard Worker 
47*c8dee2aaSAndroid Build Coastguard Worker typedef sk_sp<SkImage> (*ImageMakerProc)(GrRecordingContext*, SkPicture*, const SkImageInfo&);
48*c8dee2aaSAndroid Build Coastguard Worker 
make_raster(GrRecordingContext *,SkPicture * pic,const SkImageInfo & info)49*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_raster(GrRecordingContext*,
50*c8dee2aaSAndroid Build Coastguard Worker                                   SkPicture* pic,
51*c8dee2aaSAndroid Build Coastguard Worker                                   const SkImageInfo& info) {
52*c8dee2aaSAndroid Build Coastguard Worker     auto surface(SkSurfaces::Raster(info));
53*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->clear(0);
54*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->drawPicture(pic);
55*c8dee2aaSAndroid Build Coastguard Worker     return surface->makeImageSnapshot();
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker 
make_texture(GrRecordingContext * ctx,SkPicture * pic,const SkImageInfo & info)58*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_texture(GrRecordingContext* ctx,
59*c8dee2aaSAndroid Build Coastguard Worker                                    SkPicture* pic,
60*c8dee2aaSAndroid Build Coastguard Worker                                    const SkImageInfo& info) {
61*c8dee2aaSAndroid Build Coastguard Worker     if (!ctx) {
62*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
63*c8dee2aaSAndroid Build Coastguard Worker     }
64*c8dee2aaSAndroid Build Coastguard Worker     auto surface(SkSurfaces::RenderTarget(ctx, skgpu::Budgeted::kNo, info));
65*c8dee2aaSAndroid Build Coastguard Worker     if (!surface) {
66*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
67*c8dee2aaSAndroid Build Coastguard Worker     }
68*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->clear(0);
69*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->drawPicture(pic);
70*c8dee2aaSAndroid Build Coastguard Worker     return surface->makeImageSnapshot();
71*c8dee2aaSAndroid Build Coastguard Worker }
72*c8dee2aaSAndroid Build Coastguard Worker 
make_pict_gen(GrRecordingContext *,SkPicture * pic,const SkImageInfo & info)73*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_pict_gen(GrRecordingContext*,
74*c8dee2aaSAndroid Build Coastguard Worker                                     SkPicture* pic,
75*c8dee2aaSAndroid Build Coastguard Worker                                     const SkImageInfo& info) {
76*c8dee2aaSAndroid Build Coastguard Worker     return SkImages::DeferredFromPicture(sk_ref_sp(pic),
77*c8dee2aaSAndroid Build Coastguard Worker                                          info.dimensions(),
78*c8dee2aaSAndroid Build Coastguard Worker                                          nullptr,
79*c8dee2aaSAndroid Build Coastguard Worker                                          nullptr,
80*c8dee2aaSAndroid Build Coastguard Worker                                          SkImages::BitDepth::kU8,
81*c8dee2aaSAndroid Build Coastguard Worker                                          SkColorSpace::MakeSRGB());
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker 
make_encode_gen(GrRecordingContext * ctx,SkPicture * pic,const SkImageInfo & info)84*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_encode_gen(GrRecordingContext* ctx,
85*c8dee2aaSAndroid Build Coastguard Worker                                       SkPicture* pic,
86*c8dee2aaSAndroid Build Coastguard Worker                                       const SkImageInfo& info) {
87*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> src(make_raster(ctx, pic, info));
88*c8dee2aaSAndroid Build Coastguard Worker     if (!src) {
89*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
90*c8dee2aaSAndroid Build Coastguard Worker     }
91*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkData> encoded = SkPngEncoder::Encode(nullptr, src.get(), {});
92*c8dee2aaSAndroid Build Coastguard Worker     if (!encoded) {
93*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
94*c8dee2aaSAndroid Build Coastguard Worker     }
95*c8dee2aaSAndroid Build Coastguard Worker     return SkImages::DeferredFromEncodedData(std::move(encoded));
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker 
98*c8dee2aaSAndroid Build Coastguard Worker const ImageMakerProc gProcs[] = {
99*c8dee2aaSAndroid Build Coastguard Worker     make_raster,
100*c8dee2aaSAndroid Build Coastguard Worker     make_texture,
101*c8dee2aaSAndroid Build Coastguard Worker     make_pict_gen,
102*c8dee2aaSAndroid Build Coastguard Worker     make_encode_gen,
103*c8dee2aaSAndroid Build Coastguard Worker };
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker /*
106*c8dee2aaSAndroid Build Coastguard Worker  *  Exercise drawing pictures inside an image, showing that the image version is pixelated
107*c8dee2aaSAndroid Build Coastguard Worker  *  (correctly) when it is inside an image.
108*c8dee2aaSAndroid Build Coastguard Worker  */
109*c8dee2aaSAndroid Build Coastguard Worker class ImageShaderGM : public skiagm::GM {
110*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkPicture> fPicture;
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker public:
ImageShaderGM()113*c8dee2aaSAndroid Build Coastguard Worker     ImageShaderGM() {}
114*c8dee2aaSAndroid Build Coastguard Worker 
115*c8dee2aaSAndroid Build Coastguard Worker protected:
getName() const116*c8dee2aaSAndroid Build Coastguard Worker     SkString getName() const override { return SkString("image-shader"); }
117*c8dee2aaSAndroid Build Coastguard Worker 
getISize()118*c8dee2aaSAndroid Build Coastguard Worker     SkISize getISize() override { return SkISize::Make(850, 450); }
119*c8dee2aaSAndroid Build Coastguard Worker 
onOnceBeforeDraw()120*c8dee2aaSAndroid Build Coastguard Worker     void onOnceBeforeDraw() override {
121*c8dee2aaSAndroid Build Coastguard Worker         const SkRect bounds = SkRect::MakeWH(100, 100);
122*c8dee2aaSAndroid Build Coastguard Worker         SkPictureRecorder recorder;
123*c8dee2aaSAndroid Build Coastguard Worker         draw_something(recorder.beginRecording(bounds), bounds);
124*c8dee2aaSAndroid Build Coastguard Worker         fPicture = recorder.finishRecordingAsPicture();
125*c8dee2aaSAndroid Build Coastguard Worker     }
126*c8dee2aaSAndroid Build Coastguard Worker 
testImage(SkCanvas * canvas,SkImage * image)127*c8dee2aaSAndroid Build Coastguard Worker     void testImage(SkCanvas* canvas, SkImage* image) {
128*c8dee2aaSAndroid Build Coastguard Worker         SkAutoCanvasRestore acr(canvas, true);
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawImage(image, 0, 0);
131*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(0, 120);
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker         const SkTileMode tile = SkTileMode::kRepeat;
134*c8dee2aaSAndroid Build Coastguard Worker         const SkMatrix localM = SkMatrix::Translate(-50, -50);
135*c8dee2aaSAndroid Build Coastguard Worker         SkPaint paint;
136*c8dee2aaSAndroid Build Coastguard Worker         paint.setShader(image->makeShader(tile, tile, SkSamplingOptions(), &localM));
137*c8dee2aaSAndroid Build Coastguard Worker         paint.setAntiAlias(true);
138*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawCircle(50, 50, 50, paint);
139*c8dee2aaSAndroid Build Coastguard Worker     }
140*c8dee2aaSAndroid Build Coastguard Worker 
onDraw(SkCanvas * canvas)141*c8dee2aaSAndroid Build Coastguard Worker     void onDraw(SkCanvas* canvas) override {
142*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(20, 20);
143*c8dee2aaSAndroid Build Coastguard Worker 
144*c8dee2aaSAndroid Build Coastguard Worker         const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < std::size(gProcs); ++i) {
147*c8dee2aaSAndroid Build Coastguard Worker             sk_sp<SkImage> image(gProcs[i](canvas->recordingContext(), fPicture.get(), info));
148*c8dee2aaSAndroid Build Coastguard Worker             if (image) {
149*c8dee2aaSAndroid Build Coastguard Worker                 this->testImage(canvas, image.get());
150*c8dee2aaSAndroid Build Coastguard Worker             }
151*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(120, 0);
152*c8dee2aaSAndroid Build Coastguard Worker         }
153*c8dee2aaSAndroid Build Coastguard Worker     }
154*c8dee2aaSAndroid Build Coastguard Worker 
155*c8dee2aaSAndroid Build Coastguard Worker private:
156*c8dee2aaSAndroid Build Coastguard Worker     using INHERITED = skiagm::GM;
157*c8dee2aaSAndroid Build Coastguard Worker };
DEF_GM(return new ImageShaderGM;)158*c8dee2aaSAndroid Build Coastguard Worker DEF_GM( return new ImageShaderGM; )
159*c8dee2aaSAndroid Build Coastguard Worker 
160*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////////////////////
161*c8dee2aaSAndroid Build Coastguard Worker 
162*c8dee2aaSAndroid Build Coastguard Worker #include "tools/ToolUtils.h"
163*c8dee2aaSAndroid Build Coastguard Worker 
164*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_checker_img(int w, int h, SkColor c0, SkColor c1, int size) {
165*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap bm = ToolUtils::create_checkerboard_bitmap(w, h, c0, c1, size);
166*c8dee2aaSAndroid Build Coastguard Worker     bm.setImmutable();
167*c8dee2aaSAndroid Build Coastguard Worker     return bm.asImage();
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker 
170*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(drawimage_sampling, canvas, 500, 500) {
171*c8dee2aaSAndroid Build Coastguard Worker     constexpr int N = 256;
172*c8dee2aaSAndroid Build Coastguard Worker     constexpr float kScale = 1.0f/6;
173*c8dee2aaSAndroid Build Coastguard Worker     const SkRect dst = {0, 0, kScale*N, kScale*N};
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker     auto img = make_checker_img(N, N, SK_ColorBLACK, SK_ColorWHITE, 7)->withDefaultMipmaps();
176*c8dee2aaSAndroid Build Coastguard Worker     const SkRect src = SkRect::MakeIWH(img->width(), img->height());
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix mx = SkMatrix::RectToRect(src, dst);
179*c8dee2aaSAndroid Build Coastguard Worker 
180*c8dee2aaSAndroid Build Coastguard Worker     SkPaint paint;
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker     for (auto mm : {SkMipmapMode::kNone, SkMipmapMode::kNearest, SkMipmapMode::kLinear}) {
183*c8dee2aaSAndroid Build Coastguard Worker         for (auto fm : {SkFilterMode::kNearest, SkFilterMode::kLinear}) {
184*c8dee2aaSAndroid Build Coastguard Worker             SkSamplingOptions sampling(fm, mm);
185*c8dee2aaSAndroid Build Coastguard Worker 
186*c8dee2aaSAndroid Build Coastguard Worker             canvas->save();
187*c8dee2aaSAndroid Build Coastguard Worker 
188*c8dee2aaSAndroid Build Coastguard Worker             canvas->save();
189*c8dee2aaSAndroid Build Coastguard Worker             canvas->concat(mx);
190*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawImage(img.get(), 0, 0, sampling);
191*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
192*c8dee2aaSAndroid Build Coastguard Worker 
193*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(dst.width() + 4, 0);
194*c8dee2aaSAndroid Build Coastguard Worker 
195*c8dee2aaSAndroid Build Coastguard Worker             paint.setShader(img->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, sampling, &mx));
196*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawRect(dst, paint);
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(dst.width() + 4, 0);
199*c8dee2aaSAndroid Build Coastguard Worker 
200*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawImageRect(img.get(), src, dst, sampling, nullptr,
201*c8dee2aaSAndroid Build Coastguard Worker                                   SkCanvas::kFast_SrcRectConstraint);
202*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(0, dst.height() + 8);
205*c8dee2aaSAndroid Build Coastguard Worker         }
206*c8dee2aaSAndroid Build Coastguard Worker     }
207*c8dee2aaSAndroid Build Coastguard Worker 
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker // Test case for skbug.com/12685 (texture-backed image shaders silently fail drawing to CPU canvas)
211*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(textureimage_and_shader, canvas, 100, 50) {
212*c8dee2aaSAndroid Build Coastguard Worker     canvas->clear(SK_ColorGREEN);
213*c8dee2aaSAndroid Build Coastguard Worker 
214*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> image;
215*c8dee2aaSAndroid Build Coastguard Worker     if (canvas->getSurface()) {
216*c8dee2aaSAndroid Build Coastguard Worker         image = canvas->getSurface()->makeImageSnapshot();
217*c8dee2aaSAndroid Build Coastguard Worker         canvas->clear(SK_ColorRED);
218*c8dee2aaSAndroid Build Coastguard Worker     } else {
219*c8dee2aaSAndroid Build Coastguard Worker         auto greenSurface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(50, 50));
220*c8dee2aaSAndroid Build Coastguard Worker         greenSurface->getCanvas()->clear(SK_ColorGREEN);
221*c8dee2aaSAndroid Build Coastguard Worker         image = greenSurface->makeImageSnapshot();
222*c8dee2aaSAndroid Build Coastguard Worker     }
223*c8dee2aaSAndroid Build Coastguard Worker 
224*c8dee2aaSAndroid Build Coastguard Worker     // At this point, 'image' contains a green image. If our original canvas is GPU-backed, then
225*c8dee2aaSAndroid Build Coastguard Worker     // the snapped image will be a (GPU) texture. We will try to draw that image to a non-GPU
226*c8dee2aaSAndroid Build Coastguard Worker     // surface, to ensure that we get automatic read-back. If all goes well, we will get a pure
227*c8dee2aaSAndroid Build Coastguard Worker     // green result. If either draw fails, we'll get red (most likely).
228*c8dee2aaSAndroid Build Coastguard Worker 
229*c8dee2aaSAndroid Build Coastguard Worker     auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(50, 50));
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker     // First, use drawImage:
232*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->clear(SK_ColorRED);
233*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->drawImage(image, 0, 0);
234*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawImage(surface->makeImageSnapshot(), 0, 0);
235*c8dee2aaSAndroid Build Coastguard Worker 
236*c8dee2aaSAndroid Build Coastguard Worker     // Now, use an image shader:
237*c8dee2aaSAndroid Build Coastguard Worker     SkPaint paint;
238*c8dee2aaSAndroid Build Coastguard Worker     paint.setShader(image->makeShader(SkSamplingOptions()));
239*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->clear(SK_ColorRED);
240*c8dee2aaSAndroid Build Coastguard Worker     surface->getCanvas()->drawPaint(paint);
241*c8dee2aaSAndroid Build Coastguard Worker     canvas->drawImage(surface->makeImageSnapshot(), 50, 0);
242*c8dee2aaSAndroid Build Coastguard Worker }
243