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 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorPriv.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkEncodedImageFormat.h"
16 #include "include/core/SkFont.h"
17 #include "include/core/SkImage.h"
18 #include "include/core/SkImageEncoder.h"
19 #include "include/core/SkImageInfo.h"
20 #include "include/core/SkPaint.h"
21 #include "include/core/SkPicture.h"
22 #include "include/core/SkPictureRecorder.h"
23 #include "include/core/SkPixmap.h"
24 #include "include/core/SkRect.h"
25 #include "include/core/SkRefCnt.h"
26 #include "include/core/SkScalar.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/gpu/GrDirectContext.h"
33 #include "include/private/SkMalloc.h"
34 #include "src/core/SkAutoPixmapStorage.h"
35 #include "src/core/SkReadBuffer.h"
36 #include "src/core/SkWriteBuffer.h"
37 #include "tools/ToolUtils.h"
38
39 #include <functional>
40 #include <utility>
41
42 const SkSamplingOptions gSamplings[] = {
43 SkSamplingOptions(SkFilterMode::kNearest),
44 SkSamplingOptions(SkFilterMode::kLinear),
45 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear),
46 SkSamplingOptions(SkCubicResampler::Mitchell()),
47 };
48
drawContents(SkSurface * surface,SkColor fillC)49 static void drawContents(SkSurface* surface, SkColor fillC) {
50 SkSize size = SkSize::Make(SkIntToScalar(surface->width()),
51 SkIntToScalar(surface->height()));
52 SkCanvas* canvas = surface->getCanvas();
53
54 SkScalar stroke = size.fWidth / 10;
55 SkScalar radius = (size.fWidth - stroke) / 2;
56
57 SkPaint paint;
58
59 paint.setAntiAlias(true);
60 paint.setColor(fillC);
61 canvas->drawCircle(size.fWidth/2, size.fHeight/2, radius, paint);
62
63 paint.setStyle(SkPaint::kStroke_Style);
64 paint.setStrokeWidth(stroke);
65 paint.setColor(SK_ColorBLACK);
66 canvas->drawCircle(size.fWidth/2, size.fHeight/2, radius, paint);
67 }
68
test_surface(SkCanvas * canvas,SkSurface * surf,bool usePaint)69 static void test_surface(SkCanvas* canvas, SkSurface* surf, bool usePaint) {
70 drawContents(surf, SK_ColorRED);
71 sk_sp<SkImage> imgR = surf->makeImageSnapshot();
72
73 if (true) {
74 sk_sp<SkImage> imgR2 = surf->makeImageSnapshot();
75 SkASSERT(imgR == imgR2);
76 }
77
78 drawContents(surf, SK_ColorGREEN);
79 sk_sp<SkImage> imgG = surf->makeImageSnapshot();
80
81 // since we've drawn after we snapped imgR, imgG will be a different obj
82 SkASSERT(imgR != imgG);
83
84 drawContents(surf, SK_ColorBLUE);
85
86 SkSamplingOptions sampling;
87 SkPaint paint;
88
89 canvas->drawImage(imgR, 0, 0, sampling, usePaint ? &paint : nullptr);
90 canvas->drawImage(imgG, 0, 80, sampling, usePaint ? &paint : nullptr);
91 surf->draw(canvas, 0, 160, SkSamplingOptions(), usePaint ? &paint : nullptr);
92
93 SkRect src1, src2, src3;
94 src1.setIWH(surf->width(), surf->height());
95 src2.setLTRB(SkIntToScalar(-surf->width() / 2), SkIntToScalar(-surf->height() / 2),
96 SkIntToScalar(surf->width()), SkIntToScalar(surf->height()));
97 src3.setIWH(surf->width() / 2, surf->height() / 2);
98
99 SkRect dst1, dst2, dst3, dst4;
100 dst1.setLTRB(0, 240, 65, 305);
101 dst2.setLTRB(0, 320, 65, 385);
102 dst3.setLTRB(0, 400, 65, 465);
103 dst4.setLTRB(0, 480, 65, 545);
104
105 canvas->drawImageRect(imgR, src1, dst1, sampling, usePaint ? &paint : nullptr,
106 SkCanvas::kStrict_SrcRectConstraint);
107 canvas->drawImageRect(imgG, src2, dst2, sampling, usePaint ? &paint : nullptr,
108 SkCanvas::kStrict_SrcRectConstraint);
109 canvas->drawImageRect(imgR, src3, dst3, sampling, usePaint ? &paint : nullptr,
110 SkCanvas::kStrict_SrcRectConstraint);
111 canvas->drawImageRect(imgG, dst4, sampling, usePaint ? &paint : nullptr);
112 }
113
114 class ImageGM : public skiagm::GM {
115 void* fBuffer;
116 size_t fBufferSize;
117 SkSize fSize;
118 enum {
119 W = 64,
120 H = 64,
121 RB = W * 4 + 8,
122 };
123 public:
ImageGM()124 ImageGM() {
125 fBufferSize = RB * H;
126 fBuffer = sk_malloc_throw(fBufferSize);
127 fSize.set(SkIntToScalar(W), SkIntToScalar(H));
128 }
129
~ImageGM()130 ~ImageGM() override {
131 sk_free(fBuffer);
132 }
133
134 protected:
onShortName()135 SkString onShortName() override {
136 return SkString("image-surface");
137 }
138
onISize()139 SkISize onISize() override {
140 return SkISize::Make(960, 1200);
141 }
142
onDraw(SkCanvas * canvas)143 void onDraw(SkCanvas* canvas) override {
144 canvas->scale(2, 2);
145
146 SkFont font(ToolUtils::create_portable_typeface(), 8);
147
148 canvas->drawString("Original Img", 10, 60, font, SkPaint());
149 canvas->drawString("Modified Img", 10, 140, font, SkPaint());
150 canvas->drawString("Cur Surface", 10, 220, font, SkPaint());
151 canvas->drawString("Full Crop", 10, 300, font, SkPaint());
152 canvas->drawString("Over-crop", 10, 380, font, SkPaint());
153 canvas->drawString("Upper-left", 10, 460, font, SkPaint());
154 canvas->drawString("No Crop", 10, 540, font, SkPaint());
155
156 canvas->drawString("Pre-Alloc Img", 80, 10, font, SkPaint());
157 canvas->drawString("New Alloc Img", 160, 10, font, SkPaint());
158 canvas->drawString( "GPU", 265, 10, font, SkPaint());
159
160 canvas->translate(80, 20);
161
162 // since we draw into this directly, we need to start fresh
163 sk_bzero(fBuffer, fBufferSize);
164
165 SkImageInfo info = SkImageInfo::MakeN32Premul(W, H);
166 sk_sp<SkSurface> surf0(SkSurface::MakeRasterDirect(info, fBuffer, RB));
167 sk_sp<SkSurface> surf1(SkSurface::MakeRaster(info));
168 sk_sp<SkSurface> surf2(SkSurface::MakeRenderTarget(canvas->recordingContext(),
169 SkBudgeted::kNo, info));
170
171 test_surface(canvas, surf0.get(), true);
172 canvas->translate(80, 0);
173 test_surface(canvas, surf1.get(), true);
174 if (surf2) {
175 canvas->translate(80, 0);
176 test_surface(canvas, surf2.get(), true);
177 }
178 }
179
180 private:
181 using INHERITED = skiagm::GM;
182 };
DEF_GM(return new ImageGM;)183 DEF_GM( return new ImageGM; )
184
185 ///////////////////////////////////////////////////////////////////////////////////////////////////
186
187 static void draw_pixmap(SkCanvas* canvas, const SkPixmap& pmap) {
188 SkBitmap bitmap;
189 bitmap.installPixels(pmap);
190 canvas->drawImage(bitmap.asImage(), 0, 0);
191 }
192
show_scaled_pixels(SkCanvas * canvas,SkImage * image)193 static void show_scaled_pixels(SkCanvas* canvas, SkImage* image) {
194 SkAutoCanvasRestore acr(canvas, true);
195
196 canvas->drawImage(image, 0, 0);
197 canvas->translate(110, 10);
198
199 const SkImageInfo info = SkImageInfo::MakeN32Premul(40, 40);
200 SkAutoPixmapStorage storage;
201 storage.alloc(info);
202
203 const SkImage::CachingHint chints[] = {
204 SkImage::kAllow_CachingHint, SkImage::kDisallow_CachingHint,
205 };
206
207 for (auto ch : chints) {
208 canvas->save();
209 for (auto s : gSamplings) {
210 if (image->scalePixels(storage, s, ch)) {
211 draw_pixmap(canvas, storage);
212 }
213 canvas->translate(70, 0);
214 }
215 canvas->restore();
216 canvas->translate(0, 45);
217 }
218 }
219
draw_contents(SkCanvas * canvas)220 static void draw_contents(SkCanvas* canvas) {
221 SkPaint paint;
222 paint.setStyle(SkPaint::kStroke_Style);
223 paint.setStrokeWidth(20);
224 canvas->drawCircle(50, 50, 35, paint);
225 }
226
make_raster(const SkImageInfo & info,GrRecordingContext *,void (* draw)(SkCanvas *))227 static sk_sp<SkImage> make_raster(const SkImageInfo& info,
228 GrRecordingContext*,
229 void (*draw)(SkCanvas*)) {
230 auto surface(SkSurface::MakeRaster(info));
231 draw(surface->getCanvas());
232 return surface->makeImageSnapshot();
233 }
234
make_picture(const SkImageInfo & info,GrRecordingContext *,void (* draw)(SkCanvas *))235 static sk_sp<SkImage> make_picture(const SkImageInfo& info,
236 GrRecordingContext*,
237 void (*draw)(SkCanvas*)) {
238 SkPictureRecorder recorder;
239 draw(recorder.beginRecording(SkRect::MakeIWH(info.width(), info.height())));
240 return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
241 info.dimensions(), nullptr, nullptr, SkImage::BitDepth::kU8,
242 SkColorSpace::MakeSRGB());
243 }
244
make_codec(const SkImageInfo & info,GrRecordingContext *,void (* draw)(SkCanvas *))245 static sk_sp<SkImage> make_codec(const SkImageInfo& info,
246 GrRecordingContext*,
247 void (*draw)(SkCanvas*)) {
248 sk_sp<SkImage> image(make_raster(info, nullptr, draw));
249 return SkImage::MakeFromEncoded(image->encodeToData());
250 }
251
make_gpu(const SkImageInfo & info,GrRecordingContext * ctx,void (* draw)(SkCanvas *))252 static sk_sp<SkImage> make_gpu(const SkImageInfo& info,
253 GrRecordingContext* ctx,
254 void (*draw)(SkCanvas*)) {
255 if (!ctx) {
256 return nullptr;
257 }
258
259 auto surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));
260 if (!surface) {
261 return nullptr;
262 }
263
264 draw(surface->getCanvas());
265 return surface->makeImageSnapshot();
266 }
267
268 typedef sk_sp<SkImage> (*ImageMakerProc)(const SkImageInfo&,
269 GrRecordingContext*,
270 void (*)(SkCanvas*));
271
272 class ScalePixelsGM : public skiagm::GM {
273 public:
ScalePixelsGM()274 ScalePixelsGM() {}
275
276 protected:
onShortName()277 SkString onShortName() override {
278 return SkString("scale-pixels");
279 }
280
onISize()281 SkISize onISize() override {
282 return SkISize::Make(960, 1200);
283 }
284
onDraw(SkCanvas * canvas)285 void onDraw(SkCanvas* canvas) override {
286 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
287
288 const ImageMakerProc procs[] = {
289 make_codec, make_raster, make_picture, make_codec, make_gpu,
290 };
291 for (auto& proc : procs) {
292 sk_sp<SkImage> image(proc(info, canvas->recordingContext(), draw_contents));
293 if (image) {
294 show_scaled_pixels(canvas, image.get());
295 }
296 canvas->translate(0, 120);
297 }
298 }
299
300 private:
301 using INHERITED = skiagm::GM;
302 };
303 DEF_GM( return new ScalePixelsGM; )
304
305 ///////////////////////////////////////////////////////////////////////////////////////////////////
306
307 DEF_SIMPLE_GPU_GM(new_texture_image, rContext, canvas, 280, 60) {
308 auto dContext = rContext->asDirectContext();
309 if (!dContext) {
310 return;
311 }
312
__anon3f6f23310202(SkCanvas* canvas) 313 auto render_image = [](SkCanvas* canvas) {
314 canvas->clear(SK_ColorBLUE);
315 SkPaint paint;
316 paint.setColor(SK_ColorRED);
317 canvas->drawRect(SkRect::MakeXYWH(10.f,10.f,10.f,10.f), paint);
318 paint.setColor(SK_ColorGREEN);
319 canvas->drawRect(SkRect::MakeXYWH(30.f,10.f,10.f,10.f), paint);
320 paint.setColor(SK_ColorYELLOW);
321 canvas->drawRect(SkRect::MakeXYWH(10.f,30.f,10.f,10.f), paint);
322 paint.setColor(SK_ColorCYAN);
323 canvas->drawRect(SkRect::MakeXYWH(30.f,30.f,10.f,10.f), paint);
324 };
325
326 static constexpr int kSize = 50;
327 SkBitmap bmp;
328 bmp.allocPixels(SkImageInfo::MakeS32(kSize, kSize, kPremul_SkAlphaType));
329 SkCanvas bmpCanvas(bmp);
330 render_image(&bmpCanvas);
331
332 std::function<sk_sp<SkImage>()> imageFactories[] = {
333 // Create sw raster image.
__anon3f6f23310302null334 [bmp] {
335 return bmp.asImage();
336 },
337 // Create encoded image.
__anon3f6f23310402null338 [bmp] {
339 auto src = SkEncodeBitmap(bmp, SkEncodedImageFormat::kPNG, 100);
340 return SkImage::MakeFromEncoded(std::move(src));
341 },
342 // Create YUV encoded image.
__anon3f6f23310502null343 [bmp] {
344 auto src = SkEncodeBitmap(bmp, SkEncodedImageFormat::kJPEG, 100);
345 return SkImage::MakeFromEncoded(std::move(src));
346 },
347 // Create a picture image.
__anon3f6f23310602null348 [render_image] {
349 SkPictureRecorder recorder;
350 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kSize), SkIntToScalar(kSize));
351 render_image(canvas);
352 sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
353 return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
354 SkISize::Make(kSize, kSize), nullptr, nullptr,
355 SkImage::BitDepth::kU8, srgbColorSpace);
356 },
357 // Create a texture image
__anon3f6f23310702() 358 [rContext, render_image]() -> sk_sp<SkImage> {
359 auto surface(SkSurface::MakeRenderTarget(rContext, SkBudgeted::kYes,
360 SkImageInfo::MakeS32(kSize, kSize,
361 kPremul_SkAlphaType)));
362 if (!surface) {
363 return nullptr;
364 }
365 render_image(surface->getCanvas());
366 return surface->makeImageSnapshot();
367 }
368 };
369
370 constexpr SkScalar kPad = 5.f;
371 canvas->translate(kPad, kPad);
372 for (const auto& factory : imageFactories) {
373 sk_sp<SkImage> image(factory());
374 if (image) {
375 sk_sp<SkImage> texImage(image->makeTextureImage(dContext));
376 if (texImage) {
377 canvas->drawImage(texImage, 0, 0);
378 }
379 }
380 canvas->translate(kSize + kPad, 0);
381 }
382 }
383
draw_pixmap(SkCanvas * canvas,const SkPixmap & pm,SkScalar x,SkScalar y)384 static void draw_pixmap(SkCanvas* canvas, const SkPixmap& pm, SkScalar x, SkScalar y) {
385 canvas->drawImage(SkImage::MakeRasterCopy(pm), x, y);
386 }
387
slam_ff(const SkPixmap & pm)388 static void slam_ff(const SkPixmap& pm) {
389 for (int y = 0; y < pm.height(); ++y) {
390 for (int x = 0; x < pm.width(); ++x) {
391 *pm.writable_addr32(x, y) = *pm.addr32(x, y) | SkPackARGB32(0xFF, 0, 0, 0);
392 }
393 }
394 }
395
396 DEF_SIMPLE_GM(scalepixels_unpremul, canvas, 1080, 280) {
397 SkImageInfo info = SkImageInfo::MakeN32(16, 16, kUnpremul_SkAlphaType);
398 SkAutoPixmapStorage pm;
399 pm.alloc(info);
400 for (int y = 0; y < 16; ++y) {
401 for (int x = 0; x < 16; ++x) {
402 *pm.writable_addr32(x, y) = SkPackARGB32NoCheck(0, (y << 4) | y, (x << 4) | x, 0xFF);
403 }
404 }
405 SkAutoPixmapStorage pm2;
406 pm2.alloc(SkImageInfo::MakeN32(256, 256, kUnpremul_SkAlphaType));
407
408 for (auto s : gSamplings) {
409 pm.scalePixels(pm2, s);
410 slam_ff(pm2);
411 draw_pixmap(canvas, pm2, 10, 10);
412 canvas->translate(pm2.width() + 10.0f, 0);
413 }
414 }
415
416 ///////////////////////////////////////////////////////////////////////////////////////////////////
417
make_lazy_image(SkSurface * surf)418 static sk_sp<SkImage> make_lazy_image(SkSurface* surf) {
419 surf->getCanvas()->drawCircle(100, 100, 100, SkPaint());
420 sk_sp<SkData> data = surf->makeImageSnapshot()->encodeToData();
421 if (!data) {
422 return nullptr;
423 }
424 return SkImage::MakeFromEncoded(std::move(data));
425 }
426
serial_deserial(SkImage * img)427 static sk_sp<SkImage> serial_deserial(SkImage* img) {
428 SkBinaryWriteBuffer writer;
429 writer.writeImage(img);
430 size_t length = writer.bytesWritten();
431 auto data = SkData::MakeUninitialized(length);
432 writer.writeToMemory(data->writable_data());
433
434 SkReadBuffer reader(data->data(), length);
435 return reader.readImage();
436 }
437
438 DEF_SIMPLE_GM_CAN_FAIL(image_subset, canvas, errorMsg, 440, 220) {
439 auto rContext = canvas->recordingContext();
440 auto dContext = GrAsDirectContext(rContext);
441
442 if (!dContext && rContext) {
443 *errorMsg = "Not supported in DDL mode";
444 return skiagm::DrawResult::kSkip;
445 }
446
447 SkImageInfo info = SkImageInfo::MakeN32Premul(200, 200, nullptr);
448 auto surf = ToolUtils::makeSurface(canvas, info, nullptr);
449 auto img = make_lazy_image(surf.get());
450 if (!img) {
451 *errorMsg = "Failed to make lazy image.";
452 return skiagm::DrawResult::kFail;
453 }
454
455 canvas->drawImage(img, 10, 10);
456 auto sub = img->makeSubset({100, 100, 200, 200});
457 canvas->drawImage(sub, 220, 10);
458 sub = serial_deserial(sub.get());
459 canvas->drawImage(sub, 220+110, 10);
460 return skiagm::DrawResult::kOk;
461 }
462