xref: /aosp_15_r20/external/skia/gm/image.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 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 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorPriv.h"
14 #include "include/core/SkColorSpace.h"
15 #include "include/core/SkData.h"
16 #include "include/core/SkFont.h"
17 #include "include/core/SkImage.h"
18 #include "include/core/SkImageInfo.h"
19 #include "include/core/SkPaint.h"
20 #include "include/core/SkPicture.h"
21 #include "include/core/SkPictureRecorder.h"
22 #include "include/core/SkPixmap.h"
23 #include "include/core/SkRect.h"
24 #include "include/core/SkRefCnt.h"
25 #include "include/core/SkScalar.h"
26 #include "include/core/SkSerialProcs.h"
27 #include "include/core/SkSize.h"
28 #include "include/core/SkString.h"
29 #include "include/core/SkSurface.h"
30 #include "include/core/SkTypeface.h"
31 #include "include/core/SkTypes.h"
32 #include "include/encode/SkJpegEncoder.h"
33 #include "include/encode/SkPngEncoder.h"
34 #include "include/gpu/ganesh/GrDirectContext.h"
35 #include "include/gpu/ganesh/SkImageGanesh.h"
36 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
37 #include "include/private/base/SkMalloc.h"
38 #include "src/core/SkAutoPixmapStorage.h"
39 #include "src/core/SkReadBuffer.h"
40 #include "src/core/SkWriteBuffer.h"
41 #include "src/image/SkImage_Base.h"
42 #include "tools/GpuToolUtils.h"
43 #include "tools/ToolUtils.h"
44 #include "tools/fonts/FontToolUtils.h"
45 
46 #if defined(SK_GRAPHITE)
47 #include "include/gpu/graphite/Recorder.h"
48 #include "include/gpu/graphite/Surface.h"
49 #endif
50 
51 #include <functional>
52 #include <utility>
53 
54 #if defined(SK_GRAPHITE)
55 #include "include/gpu/graphite/Image.h"
56 #endif
57 
58 const SkSamplingOptions gSamplings[] = {
59     SkSamplingOptions(SkFilterMode::kNearest),
60     SkSamplingOptions(SkFilterMode::kLinear),
61     SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear),
62     SkSamplingOptions(SkCubicResampler::Mitchell()),
63 };
64 
draw_contents(SkSurface * surface,SkColor fillC)65 static void draw_contents(SkSurface* surface, SkColor fillC) {
66     SkSize size = SkSize::Make(SkIntToScalar(surface->width()),
67                                SkIntToScalar(surface->height()));
68     SkCanvas* canvas = surface->getCanvas();
69 
70     SkScalar stroke = size.fWidth / 10;
71     SkScalar radius = (size.fWidth - stroke) / 2;
72 
73     SkPaint paint;
74 
75     paint.setAntiAlias(true);
76     paint.setColor(fillC);
77     canvas->drawCircle(size.fWidth/2, size.fHeight/2, radius, paint);
78 
79     paint.setStyle(SkPaint::kStroke_Style);
80     paint.setStrokeWidth(stroke);
81     paint.setColor(SK_ColorBLACK);
82     canvas->drawCircle(size.fWidth/2, size.fHeight/2, radius, paint);
83 }
84 
test_surface(SkCanvas * canvas,SkSurface * surf,bool usePaint)85 static void test_surface(SkCanvas* canvas, SkSurface* surf, bool usePaint) {
86     draw_contents(surf, SK_ColorRED);
87     sk_sp<SkImage> imgR = surf->makeImageSnapshot();
88 
89     if (true) {
90         sk_sp<SkImage> imgR2 = surf->makeImageSnapshot();
91         SkASSERT(imgR == imgR2);
92     }
93 
94     imgR = ToolUtils::MakeTextureImage(canvas, std::move(imgR));
95     draw_contents(surf, SK_ColorGREEN);
96     sk_sp<SkImage> imgG = ToolUtils::MakeTextureImage(canvas, surf->makeImageSnapshot());
97 
98     // since we've drawn after we snapped imgR, imgG will be a different obj unless the
99     // gpu context has been abandoned (in which case they will both be null)
100     SkASSERT(imgR != imgG || (!imgR && !imgG));
101 
102     draw_contents(surf, SK_ColorBLUE);
103 
104     SkSamplingOptions sampling;
105     SkPaint paint;
106 
107     canvas->drawImage(imgR, 0, 0, sampling, usePaint ? &paint : nullptr);
108     canvas->drawImage(imgG, 0, 80, sampling, usePaint ? &paint : nullptr);
109     surf->draw(canvas, 0, 160, SkSamplingOptions(), usePaint ? &paint : nullptr);
110 
111     SkRect src1, src2, src3;
112     src1.setIWH(surf->width(), surf->height());
113     src2.setLTRB(SkIntToScalar(-surf->width() / 2), SkIntToScalar(-surf->height() / 2),
114                  SkIntToScalar(surf->width()),       SkIntToScalar(surf->height()));
115     src3.setIWH(surf->width() / 2, surf->height() / 2);
116 
117     SkRect dst1, dst2, dst3, dst4;
118     dst1.setLTRB(0, 240, 65, 305);
119     dst2.setLTRB(0, 320, 65, 385);
120     dst3.setLTRB(0, 400, 65, 465);
121     dst4.setLTRB(0, 480, 65, 545);
122 
123     canvas->drawImageRect(imgR, src1, dst1, sampling, usePaint ? &paint : nullptr,
124                           SkCanvas::kStrict_SrcRectConstraint);
125     canvas->drawImageRect(imgG, src2, dst2, sampling, usePaint ? &paint : nullptr,
126                           SkCanvas::kStrict_SrcRectConstraint);
127     canvas->drawImageRect(imgR, src3, dst3, sampling, usePaint ? &paint : nullptr,
128                           SkCanvas::kStrict_SrcRectConstraint);
129     canvas->drawImageRect(imgG, dst4, sampling, usePaint ? &paint : nullptr);
130 }
131 
132 class ImageGM : public skiagm::GM {
133     void*   fBuffer;
134     size_t  fBufferSize;
135     SkSize  fSize;
136     enum {
137         W = 64,
138         H = 64,
139         RB = W * 4 + 8,
140     };
141 public:
ImageGM()142     ImageGM() {
143         fBufferSize = RB * H;
144         fBuffer = sk_malloc_throw(fBufferSize);
145         fSize.set(SkIntToScalar(W), SkIntToScalar(H));
146     }
147 
~ImageGM()148     ~ImageGM() override {
149         sk_free(fBuffer);
150     }
151 
152 protected:
getName() const153     SkString getName() const override { return SkString("image-surface"); }
154 
getISize()155     SkISize getISize() override { return SkISize::Make(960, 1200); }
156 
onDraw(SkCanvas * canvas)157     void onDraw(SkCanvas* canvas) override {
158         canvas->scale(2, 2);
159 
160         SkFont font(ToolUtils::DefaultPortableTypeface(), 8);
161 
162         canvas->drawString("Original Img",  10,  60, font, SkPaint());
163         canvas->drawString("Modified Img",  10, 140, font, SkPaint());
164         canvas->drawString("Cur Surface",   10, 220, font, SkPaint());
165         canvas->drawString("Full Crop",     10, 300, font, SkPaint());
166         canvas->drawString("Over-crop",     10, 380, font, SkPaint());
167         canvas->drawString("Upper-left",    10, 460, font, SkPaint());
168         canvas->drawString("No Crop",       10, 540, font, SkPaint());
169 
170         canvas->drawString("Pre-Alloc Img", 80,  10, font, SkPaint());
171         canvas->drawString("New Alloc Img", 160, 10, font, SkPaint());
172         canvas->drawString( "GPU",          265, 10, font, SkPaint());
173 
174         canvas->translate(80, 20);
175 
176         // since we draw into this directly, we need to start fresh
177         sk_bzero(fBuffer, fBufferSize);
178 
179         SkImageInfo info = SkImageInfo::MakeN32Premul(W, H);
180         sk_sp<SkSurface> surf0(SkSurfaces::WrapPixels(info, fBuffer, RB));
181         sk_sp<SkSurface> surf1(SkSurfaces::Raster(info));
182         sk_sp<SkSurface> surf2(
183                 SkSurfaces::RenderTarget(canvas->recordingContext(), skgpu::Budgeted::kNo, info));
184 
185         test_surface(canvas, surf0.get(), true);
186         canvas->translate(80, 0);
187         test_surface(canvas, surf1.get(), true);
188         if (surf2) {
189             canvas->translate(80, 0);
190             test_surface(canvas, surf2.get(), true);
191         }
192     }
193 
194 private:
195     using INHERITED = skiagm::GM;
196 };
DEF_GM(return new ImageGM;)197 DEF_GM( return new ImageGM; )
198 
199 ///////////////////////////////////////////////////////////////////////////////////////////////////
200 
201 static void draw_pixmap(SkCanvas* canvas, const SkPixmap& pmap) {
202     SkBitmap bitmap;
203     bitmap.installPixels(pmap);
204     canvas->drawImage(bitmap.asImage(), 0, 0);
205 }
206 
show_scaled_pixels(SkCanvas * canvas,SkImage * image,bool useImageScaling)207 static void show_scaled_pixels(SkCanvas* canvas, SkImage* image, bool useImageScaling) {
208     SkAutoCanvasRestore acr(canvas, true);
209 
210     canvas->drawImage(image, 0, 0);
211     canvas->translate(110, 10);
212 
213     const SkImageInfo info = SkImageInfo::MakeN32Premul(40, 40);
214     SkAutoPixmapStorage storage;
215     storage.alloc(info);
216 
217     const SkImage::CachingHint chints[] = {
218         SkImage::kAllow_CachingHint, SkImage::kDisallow_CachingHint,
219     };
220 
221     for (auto ch : chints) {
222         canvas->save();
223         for (auto s : gSamplings) {
224             if (useImageScaling) {
225                 if (auto scaled = image->makeScaled(canvas->recorder() ,info, s)) {
226                     canvas->drawImage(scaled, 0, 0);
227                 }
228             } else {
229                 if (image->scalePixels(storage, s, ch)) {
230                     draw_pixmap(canvas, storage);
231                 }
232             }
233             canvas->translate(70, 0);
234         }
235         canvas->restore();
236         canvas->translate(0, 45);
237     }
238 }
239 
draw_contents(SkCanvas * canvas)240 static void draw_contents(SkCanvas* canvas) {
241     SkPaint paint;
242     paint.setStyle(SkPaint::kStroke_Style);
243     paint.setStrokeWidth(20);
244     canvas->drawCircle(50, 50, 35, paint);
245 }
246 
make_raster(const SkImageInfo & info,GrRecordingContext *,skgpu::graphite::Recorder *,void (* draw)(SkCanvas *))247 static sk_sp<SkImage> make_raster(const SkImageInfo& info,
248                                   GrRecordingContext*,
249                                   skgpu::graphite::Recorder*,
250                                   void (*draw)(SkCanvas*)) {
251     auto surface(SkSurfaces::Raster(info));
252     draw(surface->getCanvas());
253     return surface->makeImageSnapshot();
254 }
255 
make_picture(const SkImageInfo & info,GrRecordingContext *,skgpu::graphite::Recorder *,void (* draw)(SkCanvas *))256 static sk_sp<SkImage> make_picture(const SkImageInfo& info,
257                                    GrRecordingContext*,
258                                    skgpu::graphite::Recorder*,
259                                    void (*draw)(SkCanvas*)) {
260     SkPictureRecorder recorder;
261     draw(recorder.beginRecording(SkRect::MakeIWH(info.width(), info.height())));
262     return SkImages::DeferredFromPicture(recorder.finishRecordingAsPicture(),
263                                          info.dimensions(),
264                                          nullptr,
265                                          nullptr,
266                                          SkImages::BitDepth::kU8,
267                                          SkColorSpace::MakeSRGB());
268 }
269 
make_codec(const SkImageInfo & info,GrRecordingContext *,skgpu::graphite::Recorder *,void (* draw)(SkCanvas *))270 static sk_sp<SkImage> make_codec(const SkImageInfo& info,
271                                  GrRecordingContext*,
272                                  skgpu::graphite::Recorder*,
273                                  void (*draw)(SkCanvas*)) {
274     sk_sp<SkImage> image(make_raster(info, nullptr, nullptr, draw));
275     return SkImages::DeferredFromEncodedData(SkPngEncoder::Encode(nullptr, image.get(), {}));
276 }
277 
make_gpu(const SkImageInfo & info,GrRecordingContext * ctx,skgpu::graphite::Recorder * recorder,void (* draw)(SkCanvas *))278 static sk_sp<SkImage> make_gpu(const SkImageInfo& info,
279                                GrRecordingContext* ctx,
280                                skgpu::graphite::Recorder* recorder,
281                                void (*draw)(SkCanvas*)) {
282     sk_sp<SkSurface> surface;
283     if (ctx) {
284         surface = SkSurfaces::RenderTarget(ctx, skgpu::Budgeted::kNo, info);
285     }
286 #if defined(SK_GRAPHITE)
287     if (recorder) {
288         surface = SkSurfaces::RenderTarget(recorder, info);
289     }
290 #endif
291     if (!surface) {
292         return nullptr;
293     }
294 
295     draw(surface->getCanvas());
296     return surface->makeImageSnapshot();
297 }
298 
299 typedef sk_sp<SkImage> (*ImageMakerProc)(const SkImageInfo&,
300                                          GrRecordingContext*,
301                                          skgpu::graphite::Recorder*,
302                                          void (*)(SkCanvas*));
303 
304 class ScalePixelsGM : public skiagm::GM {
305 public:
ScalePixelsGM(bool useImageScaling)306     ScalePixelsGM(bool useImageScaling) : fUseImageScaling(useImageScaling) {}
307 
308 protected:
getName() const309     SkString getName() const override {
310         auto str = SkString("scale-pixels");
311         if (fUseImageScaling) {
312             str += "-via-image";
313         }
314         return str;
315     }
316 
getISize()317     SkISize getISize() override { return SkISize::Make(960, 1200); }
318 
onDraw(SkCanvas * canvas)319     void onDraw(SkCanvas* canvas) override {
320         const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
321 
322         const ImageMakerProc procs[] = {
323             make_codec, make_raster, make_picture, make_codec, make_gpu,
324         };
325         for (auto& proc : procs) {
326             sk_sp<SkImage> image(
327                     proc(info, canvas->recordingContext(), canvas->recorder(), draw_contents));
328             if (image) {
329                 show_scaled_pixels(canvas, image.get(), fUseImageScaling);
330             }
331             canvas->translate(0, 120);
332         }
333     }
334 
335 private:
336     using INHERITED = skiagm::GM;
337     bool fUseImageScaling;
338 };
339 DEF_GM( return new ScalePixelsGM(false); )
DEF_GM(return new ScalePixelsGM (true);)340 DEF_GM( return new ScalePixelsGM(true); )
341 
342 ///////////////////////////////////////////////////////////////////////////////////////////////////
343 
344 DEF_SIMPLE_GM_CAN_FAIL(new_texture_image, canvas, errorMsg, 280, 115) {
345 
346     GrDirectContext* dContext = GrAsDirectContext(canvas->recordingContext());
347     bool isGPU = SkToBool(dContext);
348 
349 #if defined(SK_GRAPHITE)
350     skgpu::graphite::Recorder* recorder = canvas->recorder();
351     isGPU = isGPU || SkToBool(recorder);
352 #endif
353 
354     if (!isGPU) {
355         *errorMsg = skiagm::GM::kErrorMsg_DrawSkippedGpuOnly;
356         return skiagm::DrawResult::kSkip;
357     }
358 
__anon599373350202(SkCanvas* canvas) 359     auto render_image = [](SkCanvas* canvas) {
360         canvas->clear(SK_ColorBLUE);
361         SkPaint paint;
362         paint.setColor(SK_ColorRED);
363         canvas->drawRect(SkRect::MakeXYWH(10.f,10.f,10.f,10.f), paint);
364         paint.setColor(SK_ColorGREEN);
365         canvas->drawRect(SkRect::MakeXYWH(30.f,10.f,10.f,10.f), paint);
366         paint.setColor(SK_ColorYELLOW);
367         canvas->drawRect(SkRect::MakeXYWH(10.f,30.f,10.f,10.f), paint);
368         paint.setColor(SK_ColorCYAN);
369         canvas->drawRect(SkRect::MakeXYWH(30.f,30.f,10.f,10.f), paint);
370     };
371 
372     static constexpr int kSize = 50;
373     SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
374                                        kRGBA_8888_SkColorType, kPremul_SkAlphaType,
375                                        SkColorSpace::MakeSRGB());
376     SkBitmap bmp;
377     bmp.allocPixels(ii);
378     SkCanvas bmpCanvas(bmp);
379     render_image(&bmpCanvas);
380 
381     std::function<sk_sp<SkImage>()> imageFactories[] = {
382             // Create sw raster image.
__anon599373350302null383             [&] { return bmp.asImage(); },
384             // Create encoded image.
__anon599373350402null385             [&] {
386                 SkDynamicMemoryWStream stream;
387                 SkASSERT_RELEASE(SkPngEncoder::Encode(&stream, bmp.pixmap(), {}));
388                 return SkImages::DeferredFromEncodedData(stream.detachAsData());
389             },
390             // Create YUV encoded image.
__anon599373350502null391             [&] {
392                 SkDynamicMemoryWStream stream;
393                 SkASSERT_RELEASE(SkJpegEncoder::Encode(&stream, bmp.pixmap(), {}));
394                 return SkImages::DeferredFromEncodedData(stream.detachAsData());
395             },
396             // Create a picture image.
__anon599373350602null397             [&] {
398                 SkPictureRecorder recorder;
399                 SkCanvas* canvas =
400                         recorder.beginRecording(SkIntToScalar(kSize), SkIntToScalar(kSize));
401                 render_image(canvas);
402                 sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
403                 return SkImages::DeferredFromPicture(recorder.finishRecordingAsPicture(),
404                                                      SkISize::Make(kSize, kSize),
405                                                      nullptr,
406                                                      nullptr,
407                                                      SkImages::BitDepth::kU8,
408                                                      srgbColorSpace);
409             },
410             // Create a texture image
__anon599373350702() 411             [&]() -> sk_sp<SkImage> {
412                 sk_sp<SkSurface> surface;
413                 if (dContext) {
414                     surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kYes, ii);
415                 } else {
416 #if defined(SK_GRAPHITE)
417                     surface = SkSurfaces::RenderTarget(recorder, ii);
418 #endif
419                 }
420 
421                 if (!surface) {
422                     return nullptr;
423                 }
424                 render_image(surface->getCanvas());
425                 return surface->makeImageSnapshot();
426             }};
427 
428     constexpr SkScalar kPad = 5.f;
429     canvas->translate(kPad, kPad);
430     for (const auto& factory : imageFactories) {
431         sk_sp<SkImage> image(factory());
432         if (image) {
433             for (auto mm : { false, true }) {
434                 sk_sp<SkImage> texImage;
435                 if (dContext) {
436                     texImage = SkImages::TextureFromImage(dContext,
437                                                           image,
438                                                           mm ? skgpu::Mipmapped::kYes
439                                                              : skgpu::Mipmapped::kNo);
440                 } else {
441 #if defined(SK_GRAPHITE)
442                     texImage = SkImages::TextureFromImage(recorder, image, {mm});
443 #endif
444                 }
445                 if (texImage) {
446                     canvas->drawImage(texImage, 0, mm ? kSize + kPad : 0);
447                 }
448             }
449         }
450         canvas->translate(kSize + kPad, 0);
451     }
452 
453     return skiagm::DrawResult::kOk;
454 }
455 
draw_pixmap(SkCanvas * canvas,const SkPixmap & pm,SkScalar x,SkScalar y)456 static void draw_pixmap(SkCanvas* canvas, const SkPixmap& pm, SkScalar x, SkScalar y) {
457     canvas->drawImage(SkImages::RasterFromPixmapCopy(pm), x, y);
458 }
459 
slam_ff(const SkPixmap & pm)460 static void slam_ff(const SkPixmap& pm) {
461     for (int y = 0; y < pm.height(); ++y) {
462         for (int x = 0; x < pm.width(); ++x) {
463             *pm.writable_addr32(x, y) = *pm.addr32(x, y) | SkPackARGB32(0xFF, 0, 0, 0);
464         }
465     }
466 }
467 
468 DEF_SIMPLE_GM(scalepixels_unpremul, canvas, 1080, 280) {
469     SkImageInfo info = SkImageInfo::MakeN32(16, 16, kUnpremul_SkAlphaType);
470     SkAutoPixmapStorage pm;
471     pm.alloc(info);
472     for (int y = 0; y < 16; ++y) {
473         for (int x = 0; x < 16; ++x) {
474             *pm.writable_addr32(x, y) = SkPackARGB32(0, (y << 4) | y, (x << 4) | x, 0xFF);
475         }
476     }
477     SkAutoPixmapStorage pm2;
478     pm2.alloc(SkImageInfo::MakeN32(256, 256, kUnpremul_SkAlphaType));
479 
480     for (auto s : gSamplings) {
481         pm.scalePixels(pm2, s);
482         slam_ff(pm2);
483         draw_pixmap(canvas, pm2, 10, 10);
484         canvas->translate(pm2.width() + 10.0f, 0);
485     }
486 }
487 
488 ///////////////////////////////////////////////////////////////////////////////////////////////////
489 
make_lazy_image()490 static sk_sp<SkImage> make_lazy_image() {
491     sk_sp<SkPicture> picture;
492     {
493         SkPictureRecorder recorder;
494         SkCanvas* canvas = recorder.beginRecording(SkRect::MakeIWH(200, 200));
495         canvas->drawCircle(100, 100, 100, SkPaint());
496         picture = recorder.finishRecordingAsPicture();
497     }
498 
499     return SkImages::DeferredFromPicture(std::move(picture),
500                                          {200, 200},
501                                          /* matrix= */ nullptr,
502                                          /* paint= */ nullptr,
503                                          SkImages::BitDepth::kU8,
504                                          SkColorSpace::MakeSRGB());
505 }
506 
serial_deserial(SkImage * img)507 static sk_sp<SkImage> serial_deserial(SkImage* img) {
508     if (!img) {
509         return nullptr;
510     }
511 
512     SkSerialProcs sProcs;
513     sProcs.fImageProc = [](SkImage* img, void*) -> sk_sp<SkData> {
514         return SkPngEncoder::Encode(as_IB(img)->directContext(), img, SkPngEncoder::Options{});
515     };
516     SkBinaryWriteBuffer writer(sProcs);
517 
518     writer.writeImage(img);
519     size_t length = writer.bytesWritten();
520     auto data = SkData::MakeUninitialized(length);
521     writer.writeToMemory(data->writable_data());
522 
523     SkReadBuffer reader(data->data(), length);
524     return reader.readImage();
525 }
526 
527 DEF_SIMPLE_GM_CAN_FAIL(image_subset, canvas, errorMsg, 440, 220) {
528     auto img = make_lazy_image();
529     if (!img) {
530         *errorMsg = "Failed to make lazy image.";
531         return skiagm::DrawResult::kFail;
532     }
533 
534     GrDirectContext* dContext = GrAsDirectContext(canvas->recordingContext());
535 #if defined(SK_GRAPHITE)
536     auto recorder = canvas->recorder();
537 #endif
538 
539     canvas->drawImage(img, 10, 10);
540 
541     sk_sp<SkImage> subset;
542 
543 #if defined(SK_GRAPHITE)
544     if (recorder) {
545         subset = img->makeSubset(recorder, {100, 100, 200, 200}, {});
546     } else
547 #endif
548     {
549         subset = img->makeSubset(dContext, {100, 100, 200, 200});
550     }
551 
552     canvas->drawImage(subset, 220, 10);
553     subset = serial_deserial(subset.get());
554     canvas->drawImage(subset, 220+110, 10);
555     return skiagm::DrawResult::kOk;
556 }
557