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 "include/core/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkBlendMode.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/SkColorType.h"
16 #include "include/core/SkData.h"
17 #include "include/core/SkDataTable.h"
18 #include "include/core/SkImage.h"
19 #include "include/core/SkImageGenerator.h"
20 #include "include/core/SkImageInfo.h"
21 #include "include/core/SkM44.h"
22 #include "include/core/SkPaint.h"
23 #include "include/core/SkPicture.h"
24 #include "include/core/SkPictureRecorder.h"
25 #include "include/core/SkPixmap.h"
26 #include "include/core/SkRect.h"
27 #include "include/core/SkRefCnt.h"
28 #include "include/core/SkSamplingOptions.h"
29 #include "include/core/SkScalar.h"
30 #include "include/core/SkSerialProcs.h"
31 #include "include/core/SkSize.h"
32 #include "include/core/SkStream.h"
33 #include "include/core/SkSurface.h"
34 #include "include/core/SkTypes.h"
35 #include "include/core/SkYUVAInfo.h"
36 #include "include/core/SkYUVAPixmaps.h"
37 #include "include/encode/SkPngEncoder.h"
38 #include "include/gpu/GpuTypes.h"
39 #include "include/gpu/ganesh/GrBackendSurface.h"
40 #include "include/gpu/ganesh/GrDirectContext.h"
41 #include "include/gpu/ganesh/GrTypes.h"
42 #include "include/gpu/ganesh/SkImageGanesh.h"
43 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
44 #include "include/private/SkColorData.h"
45 #include "include/private/base/SkCPUTypes.h"
46 #include "include/private/base/SkDebug.h"
47 #include "include/private/base/SkFloatingPoint.h"
48 #include "include/private/base/SkTemplates.h"
49 #include "include/private/base/SkTo.h"
50 #include "include/private/gpu/ganesh/GrImageContext.h"
51 #include "include/private/gpu/ganesh/GrTypesPriv.h"
52 #include "modules/skcms/skcms.h"
53 #include "src/core/SkAutoPixmapStorage.h"
54 #include "src/core/SkBitmapCache.h"
55 #include "src/core/SkColorSpacePriv.h"
56 #include "src/core/SkImagePriv.h"
57 #include "src/core/SkMemset.h"
58 #include "src/gpu/ResourceKey.h"
59 #include "src/gpu/ganesh/GrCaps.h"
60 #include "src/gpu/ganesh/GrDirectContextPriv.h"
61 #include "src/gpu/ganesh/GrGpu.h"
62 #include "src/gpu/ganesh/GrImageContextPriv.h"
63 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
64 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
65 #include "src/gpu/ganesh/GrTextureProxy.h"
66 #include "src/gpu/ganesh/image/GrImageUtils.h"
67 #include "src/gpu/ganesh/image/SkImage_GaneshYUVA.h"
68 #include "src/image/SkImageGeneratorPriv.h"
69 #include "src/image/SkImage_Base.h"
70 #include "src/shaders/SkImageShader.h"
71 #include "tests/CtsEnforcement.h"
72 #include "tests/Test.h"
73 #include "tools/DecodeUtils.h"
74 #include "tools/Resources.h"
75 #include "tools/ToolUtils.h"
76 #include "tools/gpu/FenceSync.h"
77 #include "tools/gpu/ManagedBackendTexture.h"
78 #include "tools/gpu/ProxyUtils.h"
79 #include "tools/gpu/TestContext.h"
80
81 #include <algorithm>
82 #include <cmath>
83 #include <cstdint>
84 #include <cstring>
85 #include <functional>
86 #include <initializer_list>
87 #include <memory>
88 #include <tuple>
89 #include <utility>
90 #include <vector>
91
92 class GrContextThreadSafeProxy;
93 class GrRecordingContext;
94 struct GrContextOptions;
95
96 using namespace sk_gpu_test;
97
read_pixels_info(SkImage * image)98 SkImageInfo read_pixels_info(SkImage* image) {
99 if (image->colorSpace()) {
100 return SkImageInfo::MakeS32(image->width(), image->height(), image->alphaType());
101 }
102
103 return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
104 }
105
106 // image `b` is assumed to be raster
assert_equal(skiatest::Reporter * reporter,GrDirectContext * dContextA,SkImage * a,const SkIRect * subsetA,SkImage * b)107 static void assert_equal(skiatest::Reporter* reporter, GrDirectContext* dContextA, SkImage* a,
108 const SkIRect* subsetA, SkImage* b) {
109 const int widthA = subsetA ? subsetA->width() : a->width();
110 const int heightA = subsetA ? subsetA->height() : a->height();
111
112 REPORTER_ASSERT(reporter, widthA == b->width());
113 REPORTER_ASSERT(reporter, heightA == b->height());
114
115 // see https://bug.skia.org/3965
116 //REPORTER_ASSERT(reporter, a->isOpaque() == b->isOpaque());
117
118 SkAutoPixmapStorage pmapA, pmapB;
119 pmapA.alloc(read_pixels_info(a));
120 pmapB.alloc(read_pixels_info(b));
121
122 const int srcX = subsetA ? subsetA->x() : 0;
123 const int srcY = subsetA ? subsetA->y() : 0;
124
125 REPORTER_ASSERT(reporter, a->readPixels(dContextA, pmapA, srcX, srcY));
126 REPORTER_ASSERT(reporter, b->readPixels(nullptr, pmapB, 0, 0));
127
128 const size_t widthBytes = widthA * 4;
129 for (int y = 0; y < heightA; ++y) {
130 REPORTER_ASSERT(reporter, !memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), widthBytes));
131 }
132 }
draw_image_test_pattern(SkCanvas * canvas)133 static void draw_image_test_pattern(SkCanvas* canvas) {
134 canvas->clear(SK_ColorWHITE);
135 SkPaint paint;
136 paint.setColor(SK_ColorBLACK);
137 canvas->drawRect(SkRect::MakeXYWH(5, 5, 10, 10), paint);
138 }
create_image()139 static sk_sp<SkImage> create_image() {
140 const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
141 auto surface(SkSurfaces::Raster(info));
142 draw_image_test_pattern(surface->getCanvas());
143 return surface->makeImageSnapshot();
144 }
create_image_data(SkImageInfo * info)145 static sk_sp<SkData> create_image_data(SkImageInfo* info) {
146 *info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
147 const size_t rowBytes = info->minRowBytes();
148 sk_sp<SkData> data(SkData::MakeUninitialized(rowBytes * info->height()));
149 {
150 SkBitmap bm;
151 bm.installPixels(*info, data->writable_data(), rowBytes);
152 SkCanvas canvas(bm);
153 draw_image_test_pattern(&canvas);
154 }
155 return data;
156 }
create_data_image()157 static sk_sp<SkImage> create_data_image() {
158 SkImageInfo info;
159 sk_sp<SkData> data(create_image_data(&info));
160 return SkImages::RasterFromData(info, std::move(data), info.minRowBytes());
161 }
create_image_large(int maxTextureSize)162 static sk_sp<SkImage> create_image_large(int maxTextureSize) {
163 const SkImageInfo info = SkImageInfo::MakeN32(maxTextureSize + 1, 32, kOpaque_SkAlphaType);
164 auto surface(SkSurfaces::Raster(info));
165 surface->getCanvas()->clear(SK_ColorWHITE);
166 SkPaint paint;
167 paint.setColor(SK_ColorBLACK);
168 surface->getCanvas()->drawRect(SkRect::MakeXYWH(4000, 2, 28000, 30), paint);
169 return surface->makeImageSnapshot();
170 }
create_picture_image()171 static sk_sp<SkImage> create_picture_image() {
172 SkPictureRecorder recorder;
173 SkCanvas* canvas = recorder.beginRecording(10, 10);
174 canvas->clear(SK_ColorCYAN);
175 return SkImages::DeferredFromPicture(recorder.finishRecordingAsPicture(),
176 SkISize::Make(10, 10),
177 nullptr,
178 nullptr,
179 SkImages::BitDepth::kU8,
180 SkColorSpace::MakeSRGB());
181 }
182 // Want to ensure that our Release is called when the owning image is destroyed
183 struct RasterDataHolder {
RasterDataHolderRasterDataHolder184 RasterDataHolder() : fReleaseCount(0) {}
185 sk_sp<SkData> fData;
186 int fReleaseCount;
ReleaseRasterDataHolder187 static void Release(const void* pixels, void* context) {
188 RasterDataHolder* self = static_cast<RasterDataHolder*>(context);
189 self->fReleaseCount++;
190 self->fData.reset();
191 }
192 };
create_rasterproc_image(RasterDataHolder * dataHolder)193 static sk_sp<SkImage> create_rasterproc_image(RasterDataHolder* dataHolder) {
194 SkASSERT(dataHolder);
195 SkImageInfo info;
196 dataHolder->fData = create_image_data(&info);
197 return SkImages::RasterFromPixmap(SkPixmap(info, dataHolder->fData->data(), info.minRowBytes()),
198 RasterDataHolder::Release,
199 dataHolder);
200 }
create_codec_image()201 static sk_sp<SkImage> create_codec_image() {
202 SkImageInfo info;
203 sk_sp<SkData> data(create_image_data(&info));
204 SkBitmap bitmap;
205 bitmap.installPixels(info, data->writable_data(), info.minRowBytes());
206 SkDynamicMemoryWStream stream;
207 SkASSERT_RELEASE(SkPngEncoder::Encode(&stream, bitmap.pixmap(), {}));
208 return SkImages::DeferredFromEncodedData(stream.detachAsData());
209 }
create_gpu_image(GrRecordingContext * rContext,bool withMips=false,skgpu::Budgeted budgeted=skgpu::Budgeted::kYes)210 static sk_sp<SkImage> create_gpu_image(GrRecordingContext* rContext,
211 bool withMips = false,
212 skgpu::Budgeted budgeted = skgpu::Budgeted::kYes) {
213 const SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
214 auto surface = SkSurfaces::RenderTarget(
215 rContext, budgeted, info, 0, kBottomLeft_GrSurfaceOrigin, nullptr, withMips);
216 draw_image_test_pattern(surface->getCanvas());
217 return surface->makeImageSnapshot();
218 }
219
test_encode(skiatest::Reporter * reporter,GrDirectContext * dContext,SkImage * image)220 static void test_encode(skiatest::Reporter* reporter, GrDirectContext* dContext, SkImage* image) {
221 const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10);
222 sk_sp<SkData> origEncoded = SkPngEncoder::Encode(dContext, image, {});
223 REPORTER_ASSERT(reporter, origEncoded);
224 REPORTER_ASSERT(reporter, origEncoded->size() > 0);
225
226 sk_sp<SkImage> decoded(SkImages::DeferredFromEncodedData(origEncoded));
227 if (!decoded) {
228 ERRORF(reporter, "failed to decode image!");
229 return;
230 }
231 REPORTER_ASSERT(reporter, decoded);
232 assert_equal(reporter, dContext, image, nullptr, decoded.get());
233
234 // Now see if we can instantiate an image from a subset of the surface/origEncoded
235
236 decoded = SkImages::DeferredFromEncodedData(origEncoded)->makeSubset(nullptr, ir);
237 REPORTER_ASSERT(reporter, decoded);
238 assert_equal(reporter, dContext, image, &ir, decoded.get());
239 }
240
DEF_TEST(ImageEncode,reporter)241 DEF_TEST(ImageEncode, reporter) {
242 test_encode(reporter, nullptr, create_image().get());
243 }
244
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageEncode_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)245 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageEncode_Gpu,
246 reporter,
247 ctxInfo,
248 CtsEnforcement::kApiLevel_T) {
249 auto dContext = ctxInfo.directContext();
250 test_encode(reporter, dContext, create_gpu_image(dContext).get());
251 }
252
DEF_TEST(Image_MakeFromRasterBitmap,reporter)253 DEF_TEST(Image_MakeFromRasterBitmap, reporter) {
254 const struct {
255 SkCopyPixelsMode fCPM;
256 bool fExpectSameAsMutable;
257 bool fExpectSameAsImmutable;
258 } recs[] = {
259 { kIfMutable_SkCopyPixelsMode, false, true },
260 { kAlways_SkCopyPixelsMode, false, false },
261 { kNever_SkCopyPixelsMode, true, true },
262 };
263 for (auto rec : recs) {
264 SkPixmap pm;
265 SkBitmap bm;
266 bm.allocN32Pixels(100, 100);
267
268 auto img = SkMakeImageFromRasterBitmap(bm, rec.fCPM);
269 REPORTER_ASSERT(reporter, img->peekPixels(&pm));
270 const bool sameMutable = pm.addr32(0, 0) == bm.getAddr32(0, 0);
271 REPORTER_ASSERT(reporter, rec.fExpectSameAsMutable == sameMutable);
272 REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameMutable);
273
274 bm.notifyPixelsChanged(); // force a new generation ID
275
276 bm.setImmutable();
277 img = SkMakeImageFromRasterBitmap(bm, rec.fCPM);
278 REPORTER_ASSERT(reporter, img->peekPixels(&pm));
279 const bool sameImmutable = pm.addr32(0, 0) == bm.getAddr32(0, 0);
280 REPORTER_ASSERT(reporter, rec.fExpectSameAsImmutable == sameImmutable);
281 REPORTER_ASSERT(reporter, (bm.getGenerationID() == img->uniqueID()) == sameImmutable);
282 }
283 }
284
285 // Test that image encoding failures do not break picture serialization/deserialization.
DEF_TEST(Image_Serialize_Encoding_Failure,reporter)286 DEF_TEST(Image_Serialize_Encoding_Failure, reporter) {
287 auto surface(SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100)));
288 surface->getCanvas()->clear(SK_ColorGREEN);
289 sk_sp<SkImage> image(surface->makeImageSnapshot());
290 REPORTER_ASSERT(reporter, image);
291
292 SkPictureRecorder recorder;
293 SkCanvas* canvas = recorder.beginRecording(100, 100);
294 canvas->drawImage(image.get(), 0, 0, SkSamplingOptions());
295 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
296 REPORTER_ASSERT(reporter, picture);
297 REPORTER_ASSERT(reporter, picture->approximateOpCount() > 0);
298
299 bool was_called = false;
300 SkSerialProcs procs;
301 procs.fImageProc = [](SkImage*, void* called) {
302 *(bool*)called = true;
303 return SkData::MakeEmpty();
304 };
305 procs.fImageCtx = &was_called;
306
307 REPORTER_ASSERT(reporter, !was_called);
308 auto data = picture->serialize(&procs);
309 REPORTER_ASSERT(reporter, was_called);
310 REPORTER_ASSERT(reporter, data && data->size() > 0);
311
312 auto deserialized = SkPicture::MakeFromData(data->data(), data->size());
313 REPORTER_ASSERT(reporter, deserialized);
314 REPORTER_ASSERT(reporter, deserialized->approximateOpCount() > 0);
315 }
316
317 // Test that a draw that only partially covers the drawing surface isn't
318 // interpreted as covering the entire drawing surface (i.e., exercise one of the
319 // conditions of SkCanvas::wouldOverwriteEntireSurface()).
DEF_TEST(Image_RetainSnapshot,reporter)320 DEF_TEST(Image_RetainSnapshot, reporter) {
321 const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0);
322 const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);
323 SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
324 auto surface(SkSurfaces::Raster(info));
325 surface->getCanvas()->clear(0xFF00FF00);
326
327 SkPMColor pixels[4];
328 memset(pixels, 0xFF, sizeof(pixels)); // init with values we don't expect
329 const SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(2, 2);
330 const size_t dstRowBytes = 2 * sizeof(SkPMColor);
331
332 sk_sp<SkImage> image1(surface->makeImageSnapshot());
333 REPORTER_ASSERT(reporter, image1->readPixels(nullptr, dstInfo, pixels, dstRowBytes, 0, 0));
334 for (size_t i = 0; i < std::size(pixels); ++i) {
335 REPORTER_ASSERT(reporter, pixels[i] == green);
336 }
337
338 SkPaint paint;
339 paint.setBlendMode(SkBlendMode::kSrc);
340 paint.setColor(SK_ColorRED);
341
342 surface->getCanvas()->drawRect(SkRect::MakeXYWH(1, 1, 1, 1), paint);
343
344 sk_sp<SkImage> image2(surface->makeImageSnapshot());
345 REPORTER_ASSERT(reporter, image2->readPixels(nullptr, dstInfo, pixels, dstRowBytes, 0, 0));
346 REPORTER_ASSERT(reporter, pixels[0] == green);
347 REPORTER_ASSERT(reporter, pixels[1] == green);
348 REPORTER_ASSERT(reporter, pixels[2] == green);
349 REPORTER_ASSERT(reporter, pixels[3] == red);
350 }
351
352 /////////////////////////////////////////////////////////////////////////////////////////////////
353
make_bitmap_mutable(SkBitmap * bm)354 static void make_bitmap_mutable(SkBitmap* bm) {
355 bm->allocN32Pixels(10, 10);
356 }
357
make_bitmap_immutable(SkBitmap * bm)358 static void make_bitmap_immutable(SkBitmap* bm) {
359 bm->allocN32Pixels(10, 10);
360 bm->setImmutable();
361 }
362
DEF_TEST(image_newfrombitmap,reporter)363 DEF_TEST(image_newfrombitmap, reporter) {
364 const struct {
365 void (*fMakeProc)(SkBitmap*);
366 bool fExpectPeekSuccess;
367 bool fExpectSharedID;
368 bool fExpectLazy;
369 } rec[] = {
370 { make_bitmap_mutable, true, false, false },
371 { make_bitmap_immutable, true, true, false },
372 };
373
374 for (size_t i = 0; i < std::size(rec); ++i) {
375 SkBitmap bm;
376 rec[i].fMakeProc(&bm);
377
378 sk_sp<SkImage> image(bm.asImage());
379 SkPixmap pmap;
380
381 const bool sharedID = (image->uniqueID() == bm.getGenerationID());
382 REPORTER_ASSERT(reporter, sharedID == rec[i].fExpectSharedID);
383
384 const bool peekSuccess = image->peekPixels(&pmap);
385 REPORTER_ASSERT(reporter, peekSuccess == rec[i].fExpectPeekSuccess);
386
387 const bool lazy = image->isLazyGenerated();
388 REPORTER_ASSERT(reporter, lazy == rec[i].fExpectLazy);
389 }
390 }
391
392 ///////////////////////////////////////////////////////////////////////////////////////////////////
393
394 /*
395 * This tests the caching (and preemptive purge) of the raster equivalent of a gpu-image.
396 * We cache it for performance when drawing into a raster surface.
397 *
398 * A cleaner test would know if each drawImage call triggered a read-back from the gpu,
399 * but we don't have that facility (at the moment) so we use a little internal knowledge
400 * of *how* the raster version is cached, and look for that.
401 */
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkImage_Ganesh2Cpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)402 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkImage_Ganesh2Cpu,
403 reporter,
404 ctxInfo,
405 CtsEnforcement::kApiLevel_T) {
406 SkImageInfo info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
407 sk_sp<SkImage> image(create_gpu_image(ctxInfo.directContext()));
408 const auto desc = SkBitmapCacheDesc::Make(image.get());
409
410 auto surface(SkSurfaces::Raster(info));
411
412 // now we can test drawing a gpu-backed image into a cpu-backed surface
413
414 {
415 SkBitmap cachedBitmap;
416 REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &cachedBitmap));
417 }
418
419 surface->getCanvas()->drawImage(image, 0, 0);
420 {
421 SkBitmap cachedBitmap;
422 if (SkBitmapCache::Find(desc, &cachedBitmap)) {
423 REPORTER_ASSERT(reporter, cachedBitmap.isImmutable());
424 REPORTER_ASSERT(reporter, cachedBitmap.getPixels());
425 } else {
426 // unexpected, but not really a bug, since the cache is global and this test may be
427 // run w/ other threads competing for its budget.
428 SkDebugf("SkImage_Ganesh2Cpu : cachedBitmap was already purged\n");
429 }
430 }
431
432 image.reset(nullptr);
433 {
434 SkBitmap cachedBitmap;
435 REPORTER_ASSERT(reporter, !SkBitmapCache::Find(desc, &cachedBitmap));
436 }
437 }
438
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkImage_makeTextureImage,reporter,contextInfo,CtsEnforcement::kApiLevel_T)439 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkImage_makeTextureImage,
440 reporter,
441 contextInfo,
442 CtsEnforcement::kApiLevel_T) {
443 auto dContext = contextInfo.directContext();
444 sk_gpu_test::TestContext* testContext = contextInfo.testContext();
445 GrContextFactory otherFactory;
446 ContextInfo otherContextInfo = otherFactory.getContextInfo(contextInfo.type());
447 testContext->makeCurrent();
448 std::function<sk_sp<SkImage>()> imageFactories[] = {
449 create_image,
450 create_codec_image,
451 create_data_image,
452 // Create an image from a picture.
453 create_picture_image,
454 // Create a texture image.
455 [dContext] { return create_gpu_image(dContext, true, skgpu::Budgeted::kYes); },
456 [dContext] { return create_gpu_image(dContext, false, skgpu::Budgeted::kNo); },
457 // Create a texture image in a another context.
458 [otherContextInfo] {
459 auto restore = otherContextInfo.testContext()->makeCurrentAndAutoRestore();
460 auto otherContextImage = create_gpu_image(otherContextInfo.directContext());
461 otherContextInfo.directContext()->flushAndSubmit();
462 return otherContextImage;
463 }};
464 for (auto mipmapped : {skgpu::Mipmapped::kNo, skgpu::Mipmapped::kYes}) {
465 for (const auto& factory : imageFactories) {
466 sk_sp<SkImage> image(factory());
467 if (!image) {
468 ERRORF(reporter, "Error creating image.");
469 continue;
470 }
471 GrTextureProxy* origProxy = nullptr;
472 bool origIsMippedTexture = false;
473
474 if ((origProxy = sk_gpu_test::GetTextureImageProxy(image.get(), dContext))) {
475 REPORTER_ASSERT(
476 reporter,
477 (origProxy->mipmapped() == skgpu::Mipmapped::kYes) == image->hasMipmaps());
478 origIsMippedTexture = image->hasMipmaps();
479 }
480 for (auto budgeted : {skgpu::Budgeted::kNo, skgpu::Budgeted::kYes}) {
481 auto texImage = SkImages::TextureFromImage(dContext, image, mipmapped, budgeted);
482 if (!texImage) {
483 auto imageContext = as_IB(image)->context();
484 // We expect to fail if image comes from a different context
485 if (!image->isTextureBacked() || imageContext->priv().matches(dContext)) {
486 ERRORF(reporter, "makeTextureImage failed.");
487 }
488 continue;
489 }
490 if (!texImage->isTextureBacked()) {
491 ERRORF(reporter, "makeTextureImage returned non-texture image.");
492 continue;
493 }
494
495 GrTextureProxy* copyProxy = sk_gpu_test::GetTextureImageProxy(texImage.get(),
496 dContext);
497 SkASSERT(copyProxy);
498 // Did we ask for MIPs on a context that supports them?
499 bool validRequestForMips = (mipmapped == skgpu::Mipmapped::kYes &&
500 dContext->priv().caps()->mipmapSupport());
501 // Do we expect the "copy" to have MIPs?
502 bool shouldBeMipped = origIsMippedTexture || validRequestForMips;
503 REPORTER_ASSERT(reporter, shouldBeMipped == texImage->hasMipmaps());
504 REPORTER_ASSERT(
505 reporter,
506 shouldBeMipped == (copyProxy->mipmapped() == skgpu::Mipmapped::kYes));
507
508 // We should only make a copy of an already texture-backed image if it didn't
509 // already have MIPs but we asked for MIPs and the context supports it.
510 if (image->isTextureBacked() && (!validRequestForMips || origIsMippedTexture)) {
511 if (origProxy->underlyingUniqueID() != copyProxy->underlyingUniqueID()) {
512 ERRORF(reporter, "makeTextureImage made unnecessary texture copy.");
513 }
514 } else {
515 GrTextureProxy* texProxy = sk_gpu_test::GetTextureImageProxy(texImage.get(),
516 dContext);
517 REPORTER_ASSERT(reporter, !texProxy->getUniqueKey().isValid());
518 REPORTER_ASSERT(reporter, texProxy->isBudgeted() == budgeted);
519 }
520 if (image->width() != texImage->width() || image->height() != texImage->height()) {
521 ERRORF(reporter, "makeTextureImage changed the image size.");
522 }
523 if (image->alphaType() != texImage->alphaType()) {
524 ERRORF(reporter, "makeTextureImage changed image alpha type.");
525 }
526 }
527 }
528 }
529 dContext->flushAndSubmit();
530 }
531
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkImage_makeNonTextureImage,reporter,contextInfo,CtsEnforcement::kApiLevel_T)532 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkImage_makeNonTextureImage,
533 reporter,
534 contextInfo,
535 CtsEnforcement::kApiLevel_T) {
536 auto dContext = contextInfo.directContext();
537
538 std::function<sk_sp<SkImage>()> imageFactories[] = {
539 create_image,
540 create_codec_image,
541 create_data_image,
542 create_picture_image,
543 [dContext] { return create_gpu_image(dContext); },
544 };
545 for (const auto& factory : imageFactories) {
546 sk_sp<SkImage> image = factory();
547 if (!image->isTextureBacked()) {
548 REPORTER_ASSERT(reporter, image->makeNonTextureImage().get() == image.get());
549 if (!(image = SkImages::TextureFromImage(dContext, image))) {
550 continue;
551 }
552 }
553 auto rasterImage = image->makeNonTextureImage();
554 if (!rasterImage) {
555 ERRORF(reporter, "makeNonTextureImage failed for texture-backed image.");
556 }
557 REPORTER_ASSERT(reporter, !rasterImage->isTextureBacked());
558 assert_equal(reporter, dContext, image.get(), nullptr, rasterImage.get());
559 }
560 }
561
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrContext_colorTypeSupportedAsImage,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)562 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrContext_colorTypeSupportedAsImage,
563 reporter,
564 ctxInfo,
565 CtsEnforcement::kApiLevel_T) {
566 using namespace skgpu;
567
568 auto dContext = ctxInfo.directContext();
569
570 Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
571
572 static constexpr int kSize = 10;
573
574 for (int ct = 0; ct < kLastEnum_SkColorType; ++ct) {
575 SkColorType colorType = static_cast<SkColorType>(ct);
576 bool can = dContext->colorTypeSupportedAsImage(colorType);
577
578 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(
579 dContext, kSize, kSize, colorType, skgpu::Mipmapped::kNo, GrRenderable::kNo,
580 isProtected);
581 sk_sp<SkImage> img;
582 if (mbet) {
583 img = SkImages::BorrowTextureFrom(dContext,
584 mbet->texture(),
585 kTopLeft_GrSurfaceOrigin,
586 colorType,
587 kOpaque_SkAlphaType,
588 nullptr);
589 }
590 REPORTER_ASSERT(reporter, can == SkToBool(img),
591 "colorTypeSupportedAsImage:%d, actual:%d, ct:%d", can, SkToBool(img),
592 colorType);
593 }
594 }
595
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(UnpremulTextureImage,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)596 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(UnpremulTextureImage,
597 reporter,
598 ctxInfo,
599 CtsEnforcement::kApiLevel_T) {
600 SkBitmap bmp;
601 bmp.allocPixels(
602 SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, nullptr));
603 for (int y = 0; y < 256; ++y) {
604 for (int x = 0; x < 256; ++x) {
605 *bmp.getAddr32(x, y) =
606 SkColorSetARGB((U8CPU)y, 255 - (U8CPU)y, (U8CPU)x, 255 - (U8CPU)x);
607 }
608 }
609 auto dContext = ctxInfo.directContext();
610 auto texImage = SkImages::TextureFromImage(dContext, bmp.asImage());
611 if (!texImage || texImage->alphaType() != kUnpremul_SkAlphaType) {
612 ERRORF(reporter, "Failed to make unpremul texture image.");
613 return;
614 }
615 SkBitmap unpremul;
616 unpremul.allocPixels(SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType,
617 kUnpremul_SkAlphaType, nullptr));
618 if (!texImage->readPixels(dContext, unpremul.info(), unpremul.getPixels(), unpremul.rowBytes(),
619 0, 0)) {
620 ERRORF(reporter, "Unpremul readback failed.");
621 return;
622 }
623 for (int y = 0; y < 256; ++y) {
624 for (int x = 0; x < 256; ++x) {
625 if (*bmp.getAddr32(x, y) != *unpremul.getAddr32(x, y)) {
626 ERRORF(reporter, "unpremul(0x%08x)->unpremul(0x%08x) at %d, %d.",
627 *bmp.getAddr32(x, y), *unpremul.getAddr32(x, y), x, y);
628 return;
629 }
630 }
631 }
632 SkBitmap premul;
633 premul.allocPixels(
634 SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr));
635 if (!texImage->readPixels(dContext, premul.info(), premul.getPixels(), premul.rowBytes(),
636 0, 0)) {
637 ERRORF(reporter, "Unpremul readback failed.");
638 return;
639 }
640 for (int y = 0; y < 256; ++y) {
641 for (int x = 0; x < 256; ++x) {
642 uint32_t origColor = *bmp.getAddr32(x, y);
643 int32_t origA = (origColor >> 24) & 0xff;
644 float a = origA / 255.f;
645 int32_t origB = sk_float_round2int(((origColor >> 16) & 0xff) * a);
646 int32_t origG = sk_float_round2int(((origColor >> 8) & 0xff) * a);
647 int32_t origR = sk_float_round2int(((origColor >> 0) & 0xff) * a);
648
649 uint32_t read = *premul.getAddr32(x, y);
650 int32_t readA = (read >> 24) & 0xff;
651 int32_t readB = (read >> 16) & 0xff;
652 int32_t readG = (read >> 8) & 0xff;
653 int32_t readR = (read >> 0) & 0xff;
654 // We expect that alpha=1 and alpha=0 should come out exact. Otherwise allow a little
655 // bit of tolerance for GPU vs CPU premul math.
656 int32_t tol = (origA == 0 || origA == 255) ? 0 : 1;
657 if (origA != readA || SkTAbs(readB - origB) > tol || SkTAbs(readG - origG) > tol ||
658 SkTAbs(readR - origR) > tol) {
659 ERRORF(reporter, "unpremul(0x%08x)->premul(0x%08x) expected(0x%08x) at %d, %d.",
660 *bmp.getAddr32(x, y), *premul.getAddr32(x, y), origColor, x, y);
661 return;
662 }
663 }
664 }
665 }
666
DEF_GANESH_TEST(AbandonedContextImage,reporter,options,CtsEnforcement::kApiLevel_T)667 DEF_GANESH_TEST(AbandonedContextImage, reporter, options, CtsEnforcement::kApiLevel_T) {
668 using Factory = sk_gpu_test::GrContextFactory;
669 for (int ct = 0; ct < skgpu::kContextTypeCount; ++ct) {
670 auto type = static_cast<Factory::ContextType>(ct);
671 std::unique_ptr<Factory> factory(new Factory);
672 if (!factory->get(type)) {
673 continue;
674 }
675
676 sk_sp<SkImage> img;
677 auto gsurf = SkSurfaces::RenderTarget(
678 factory->get(type),
679 skgpu::Budgeted::kYes,
680 SkImageInfo::Make(100, 100, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
681 1,
682 nullptr);
683 if (!gsurf) {
684 continue;
685 }
686 img = gsurf->makeImageSnapshot();
687 gsurf.reset();
688
689 auto rsurf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100));
690
691 REPORTER_ASSERT(reporter, img->isValid(factory->get(type)));
692 REPORTER_ASSERT(reporter, img->isValid(rsurf->getCanvas()->recordingContext()));
693
694 factory->get(type)->abandonContext();
695 REPORTER_ASSERT(reporter, !img->isValid(factory->get(type)));
696 REPORTER_ASSERT(reporter, !img->isValid(rsurf->getCanvas()->recordingContext()));
697 // This shouldn't crash.
698 rsurf->getCanvas()->drawImage(img, 0, 0);
699
700 // Give up all other refs on the context.
701 factory.reset(nullptr);
702 REPORTER_ASSERT(reporter, !img->isValid(rsurf->getCanvas()->recordingContext()));
703 // This shouldn't crash.
704 rsurf->getCanvas()->drawImage(img, 0, 0);
705 }
706 }
707
708 class EmptyGenerator : public SkImageGenerator {
709 public:
EmptyGenerator()710 EmptyGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
711 };
712
DEF_TEST(ImageEmpty,reporter)713 DEF_TEST(ImageEmpty, reporter) {
714 const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
715 SkPixmap pmap(info, nullptr, 0);
716 REPORTER_ASSERT(reporter, nullptr == SkImages::RasterFromPixmapCopy(pmap));
717 REPORTER_ASSERT(reporter, nullptr == SkImages::RasterFromData(info, nullptr, 0));
718 REPORTER_ASSERT(reporter, nullptr == SkImages::RasterFromPixmap(pmap, nullptr, nullptr));
719 REPORTER_ASSERT(reporter,
720 nullptr == SkImages::DeferredFromGenerator(std::make_unique<EmptyGenerator>()));
721 }
722
DEF_TEST(ImageDataRef,reporter)723 DEF_TEST(ImageDataRef, reporter) {
724 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
725 size_t rowBytes = info.minRowBytes();
726 size_t size = info.computeByteSize(rowBytes);
727 sk_sp<SkData> data = SkData::MakeUninitialized(size);
728 REPORTER_ASSERT(reporter, data->unique());
729 sk_sp<SkImage> image = SkImages::RasterFromData(info, data, rowBytes);
730 REPORTER_ASSERT(reporter, !data->unique());
731 image.reset();
732 REPORTER_ASSERT(reporter, data->unique());
733 }
734
has_pixels(const SkPMColor pixels[],int count,SkPMColor expected)735 static bool has_pixels(const SkPMColor pixels[], int count, SkPMColor expected) {
736 for (int i = 0; i < count; ++i) {
737 if (pixels[i] != expected) {
738 return false;
739 }
740 }
741 return true;
742 }
743
image_test_read_pixels(GrDirectContext * dContext,skiatest::Reporter * reporter,SkImage * image)744 static void image_test_read_pixels(GrDirectContext* dContext, skiatest::Reporter* reporter,
745 SkImage* image) {
746 if (!image) {
747 ERRORF(reporter, "Failed to create image!");
748 return;
749 }
750 const SkPMColor expected = SkPreMultiplyColor(SK_ColorWHITE);
751 const SkPMColor notExpected = ~expected;
752
753 const int w = 2, h = 2;
754 const size_t rowBytes = w * sizeof(SkPMColor);
755 SkPMColor pixels[w*h];
756
757 SkImageInfo info;
758
759 info = SkImageInfo::MakeUnknown(w, h);
760 REPORTER_ASSERT(reporter, !image->readPixels(dContext, info, pixels, rowBytes, 0, 0));
761
762 // out-of-bounds should fail
763 info = SkImageInfo::MakeN32Premul(w, h);
764 REPORTER_ASSERT(reporter, !image->readPixels(dContext, info, pixels, rowBytes, -w, 0));
765 REPORTER_ASSERT(reporter, !image->readPixels(dContext, info, pixels, rowBytes, 0, -h));
766 REPORTER_ASSERT(reporter, !image->readPixels(dContext, info, pixels, rowBytes,
767 image->width(), 0));
768 REPORTER_ASSERT(reporter, !image->readPixels(dContext, info, pixels, rowBytes,
769 0, image->height()));
770
771 // top-left should succeed
772 SkOpts::memset32(pixels, notExpected, w*h);
773 REPORTER_ASSERT(reporter, image->readPixels(dContext, info, pixels, rowBytes, 0, 0));
774 REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
775
776 // bottom-right should succeed
777 SkOpts::memset32(pixels, notExpected, w*h);
778 REPORTER_ASSERT(reporter, image->readPixels(dContext, info, pixels, rowBytes,
779 image->width() - w, image->height() - h));
780 REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
781
782 // partial top-left should succeed
783 SkOpts::memset32(pixels, notExpected, w*h);
784 REPORTER_ASSERT(reporter, image->readPixels(dContext, info, pixels, rowBytes, -1, -1));
785 REPORTER_ASSERT(reporter, pixels[3] == expected);
786 REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected));
787
788 // partial bottom-right should succeed
789 SkOpts::memset32(pixels, notExpected, w*h);
790 REPORTER_ASSERT(reporter, image->readPixels(dContext, info, pixels, rowBytes,
791 image->width() - 1, image->height() - 1));
792 REPORTER_ASSERT(reporter, pixels[0] == expected);
793 REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected));
794 }
DEF_TEST(ImageReadPixels,reporter)795 DEF_TEST(ImageReadPixels, reporter) {
796 sk_sp<SkImage> image(create_image());
797 image_test_read_pixels(nullptr, reporter, image.get());
798
799 image = create_data_image();
800 image_test_read_pixels(nullptr, reporter, image.get());
801
802 RasterDataHolder dataHolder;
803 image = create_rasterproc_image(&dataHolder);
804 image_test_read_pixels(nullptr, reporter, image.get());
805 image.reset();
806 REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
807
808 image = create_codec_image();
809 image_test_read_pixels(nullptr, reporter, image.get());
810 }
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageReadPixels_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)811 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageReadPixels_Gpu,
812 reporter,
813 ctxInfo,
814 CtsEnforcement::kApiLevel_T) {
815 auto dContext = ctxInfo.directContext();
816 image_test_read_pixels(dContext, reporter, create_gpu_image(dContext).get());
817 }
818
check_legacy_bitmap(skiatest::Reporter * reporter,GrDirectContext * dContext,const SkImage * image,const SkBitmap & bitmap)819 static void check_legacy_bitmap(skiatest::Reporter* reporter, GrDirectContext* dContext,
820 const SkImage* image, const SkBitmap& bitmap) {
821 REPORTER_ASSERT(reporter, image->width() == bitmap.width());
822 REPORTER_ASSERT(reporter, image->height() == bitmap.height());
823 REPORTER_ASSERT(reporter, image->alphaType() == bitmap.alphaType());
824
825 REPORTER_ASSERT(reporter, bitmap.isImmutable());
826
827 REPORTER_ASSERT(reporter, bitmap.getPixels());
828
829 const SkImageInfo info = SkImageInfo::MakeN32(1, 1, bitmap.alphaType());
830 SkPMColor imageColor;
831 REPORTER_ASSERT(reporter, image->readPixels(dContext, info, &imageColor, sizeof(SkPMColor),
832 0, 0));
833 REPORTER_ASSERT(reporter, imageColor == *bitmap.getAddr32(0, 0));
834 }
835
test_legacy_bitmap(skiatest::Reporter * reporter,GrDirectContext * dContext,const SkImage * image)836 static void test_legacy_bitmap(skiatest::Reporter* reporter, GrDirectContext* dContext,
837 const SkImage* image) {
838 if (!image) {
839 ERRORF(reporter, "Failed to create image.");
840 return;
841 }
842 SkBitmap bitmap;
843 REPORTER_ASSERT(reporter, image->asLegacyBitmap(&bitmap));
844 check_legacy_bitmap(reporter, dContext, image, bitmap);
845
846 // Test subsetting to exercise the rowBytes logic.
847 SkBitmap tmp;
848 REPORTER_ASSERT(reporter, bitmap.extractSubset(&tmp, SkIRect::MakeWH(image->width() / 2,
849 image->height() / 2)));
850 sk_sp<SkImage> subsetImage(tmp.asImage());
851 REPORTER_ASSERT(reporter, subsetImage.get());
852
853 SkBitmap subsetBitmap;
854 REPORTER_ASSERT(reporter, subsetImage->asLegacyBitmap(&subsetBitmap));
855 check_legacy_bitmap(reporter, nullptr, subsetImage.get(), subsetBitmap);
856 }
DEF_TEST(ImageLegacyBitmap,reporter)857 DEF_TEST(ImageLegacyBitmap, reporter) {
858 sk_sp<SkImage> image(create_image());
859 test_legacy_bitmap(reporter, nullptr, image.get());
860
861 image = create_data_image();
862 test_legacy_bitmap(reporter, nullptr, image.get());
863
864 RasterDataHolder dataHolder;
865 image = create_rasterproc_image(&dataHolder);
866 test_legacy_bitmap(reporter, nullptr, image.get());
867 image.reset();
868 REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
869
870 image = create_codec_image();
871 test_legacy_bitmap(reporter, nullptr, image.get());
872 }
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageLegacyBitmap_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)873 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageLegacyBitmap_Gpu,
874 reporter,
875 ctxInfo,
876 CtsEnforcement::kApiLevel_T) {
877 auto dContext = ctxInfo.directContext();
878 sk_sp<SkImage> image(create_gpu_image(dContext));
879 test_legacy_bitmap(reporter, dContext, image.get());
880 }
881
test_peek(skiatest::Reporter * reporter,SkImage * image,bool expectPeekSuccess)882 static void test_peek(skiatest::Reporter* reporter, SkImage* image, bool expectPeekSuccess) {
883 if (!image) {
884 ERRORF(reporter, "Failed to create image!");
885 return;
886 }
887 SkPixmap pm;
888 bool success = image->peekPixels(&pm);
889 REPORTER_ASSERT(reporter, expectPeekSuccess == success);
890 if (success) {
891 const SkImageInfo& info = pm.info();
892 REPORTER_ASSERT(reporter, 20 == info.width());
893 REPORTER_ASSERT(reporter, 20 == info.height());
894 REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType());
895 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() ||
896 kOpaque_SkAlphaType == info.alphaType());
897 REPORTER_ASSERT(reporter, info.minRowBytes() <= pm.rowBytes());
898 REPORTER_ASSERT(reporter, SkPreMultiplyColor(SK_ColorWHITE) == *pm.addr32(0, 0));
899 }
900 }
DEF_TEST(ImagePeek,reporter)901 DEF_TEST(ImagePeek, reporter) {
902 sk_sp<SkImage> image(create_image());
903 test_peek(reporter, image.get(), true);
904
905 image = create_data_image();
906 test_peek(reporter, image.get(), true);
907
908 RasterDataHolder dataHolder;
909 image = create_rasterproc_image(&dataHolder);
910 test_peek(reporter, image.get(), true);
911 image.reset();
912 REPORTER_ASSERT(reporter, 1 == dataHolder.fReleaseCount);
913
914 image = create_codec_image();
915 test_peek(reporter, image.get(), false);
916 }
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImagePeek_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)917 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImagePeek_Gpu,
918 reporter,
919 ctxInfo,
920 CtsEnforcement::kApiLevel_T) {
921 sk_sp<SkImage> image(create_gpu_image(ctxInfo.directContext()));
922 test_peek(reporter, image.get(), false);
923 }
924
925 struct TextureReleaseChecker {
TextureReleaseCheckerTextureReleaseChecker926 TextureReleaseChecker() : fReleaseCount(0) {}
927 int fReleaseCount;
ReleaseTextureReleaseChecker928 static void Release(void* self) {
929 static_cast<TextureReleaseChecker*>(self)->fReleaseCount++;
930 }
931 };
932
DEF_GANESH_TEST_FOR_GL_CONTEXT(SkImage_NewFromTextureRelease,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)933 DEF_GANESH_TEST_FOR_GL_CONTEXT(SkImage_NewFromTextureRelease,
934 reporter,
935 ctxInfo,
936 CtsEnforcement::kApiLevel_T) {
937 const int kWidth = 10;
938 const int kHeight = 10;
939
940 auto dContext = ctxInfo.directContext();
941
942 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(dContext,
943 kWidth,
944 kHeight,
945 kRGBA_8888_SkColorType,
946 skgpu::Mipmapped::kNo,
947 GrRenderable::kNo,
948 GrProtected::kNo);
949 if (!mbet) {
950 ERRORF(reporter, "couldn't create backend texture\n");
951 return;
952 }
953
954 TextureReleaseChecker releaseChecker;
955 GrSurfaceOrigin texOrigin = kBottomLeft_GrSurfaceOrigin;
956 sk_sp<SkImage> refImg = SkImages::BorrowTextureFrom(
957 dContext,
958 mbet->texture(),
959 texOrigin,
960 kRGBA_8888_SkColorType,
961 kPremul_SkAlphaType,
962 /*color space*/ nullptr,
963 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
964 mbet->releaseContext(TextureReleaseChecker::Release, &releaseChecker));
965
966 GrSurfaceOrigin readBackOrigin;
967 GrBackendTexture readBackBackendTex;
968 REPORTER_ASSERT(reporter,
969 SkImages::GetBackendTextureFromImage(
970 refImg, &readBackBackendTex, false, &readBackOrigin),
971 "Did not get backend texture");
972 if (!GrBackendTexture::TestingOnly_Equals(readBackBackendTex, mbet->texture())) {
973 ERRORF(reporter, "backend mismatch\n");
974 }
975 REPORTER_ASSERT(reporter,
976 GrBackendTexture::TestingOnly_Equals(readBackBackendTex, mbet->texture()));
977 if (readBackOrigin != texOrigin) {
978 ERRORF(reporter, "origin mismatch %d %d\n", readBackOrigin, texOrigin);
979 }
980 REPORTER_ASSERT(reporter, readBackOrigin == texOrigin);
981
982 // Now exercise the release proc
983 REPORTER_ASSERT(reporter, 0 == releaseChecker.fReleaseCount);
984 refImg.reset(nullptr); // force a release of the image
985 REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount);
986 }
987
test_cross_context_image(skiatest::Reporter * reporter,const GrContextOptions & options,const char * testName,const std::function<sk_sp<SkImage> (GrDirectContext *)> & imageMaker)988 static void test_cross_context_image(
989 skiatest::Reporter* reporter,
990 const GrContextOptions& options,
991 const char* testName,
992 const std::function<sk_sp<SkImage>(GrDirectContext*)>& imageMaker) {
993 for (int i = 0; i < skgpu::kContextTypeCount; ++i) {
994 GrContextFactory testFactory(options);
995 skgpu::ContextType ctxType = static_cast<skgpu::ContextType>(i);
996 ContextInfo ctxInfo = testFactory.getContextInfo(ctxType);
997 auto dContext = ctxInfo.directContext();
998 if (!dContext) {
999 continue;
1000 }
1001
1002 // If we don't have proper support for this feature, the factory will fallback to returning
1003 // codec-backed images. Those will "work", but some of our checks will fail because we
1004 // expect the cross-context images not to work on multiple contexts at once.
1005 if (!dContext->priv().caps()->crossContextTextureSupport()) {
1006 continue;
1007 }
1008
1009 // We test three lifetime patterns for a single context:
1010 // 1) Create image, free image
1011 // 2) Create image, draw, flush, free image
1012 // 3) Create image, draw, free image, flush
1013 // ... and then repeat the last two patterns with drawing on a second* context:
1014 // 4) Create image, draw*, flush*, free image
1015 // 5) Create image, draw*, free image, flush*
1016
1017 // Case #1: Create image, free image
1018 {
1019 sk_sp<SkImage> refImg(imageMaker(dContext));
1020 refImg.reset(nullptr); // force a release of the image
1021 }
1022
1023 SkImageInfo info = SkImageInfo::MakeN32Premul(128, 128);
1024 sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info);
1025 if (!surface) {
1026 ERRORF(reporter, "SkSurfaces::RenderTarget failed for %s.", testName);
1027 continue;
1028 }
1029
1030 SkCanvas* canvas = surface->getCanvas();
1031
1032 // Case #2: Create image, draw, flush, free image
1033 {
1034 sk_sp<SkImage> refImg(imageMaker(dContext));
1035
1036 canvas->drawImage(refImg, 0, 0);
1037 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
1038
1039 refImg.reset(nullptr); // force a release of the image
1040 }
1041
1042 // Case #3: Create image, draw, free image, flush
1043 {
1044 sk_sp<SkImage> refImg(imageMaker(dContext));
1045
1046 canvas->drawImage(refImg, 0, 0);
1047 refImg.reset(nullptr); // force a release of the image
1048
1049 dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
1050 }
1051
1052 // Configure second context
1053 sk_gpu_test::TestContext* testContext = ctxInfo.testContext();
1054
1055 ContextInfo otherContextInfo = testFactory.getSharedContextInfo(dContext);
1056 auto otherCtx = otherContextInfo.directContext();
1057 sk_gpu_test::TestContext* otherTestContext = otherContextInfo.testContext();
1058
1059 // Creating a context in a share group may fail
1060 if (!otherCtx) {
1061 continue;
1062 }
1063
1064 surface = SkSurfaces::RenderTarget(otherCtx, skgpu::Budgeted::kNo, info);
1065 canvas = surface->getCanvas();
1066
1067 // Case #4: Create image, draw*, flush*, free image
1068 {
1069 testContext->makeCurrent();
1070 sk_sp<SkImage> refImg(imageMaker(dContext));
1071
1072 otherTestContext->makeCurrent();
1073 canvas->drawImage(refImg, 0, 0);
1074 otherCtx->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
1075
1076 testContext->makeCurrent();
1077 refImg.reset(nullptr); // force a release of the image
1078 }
1079
1080 // Case #5: Create image, draw*, free image, flush*
1081 {
1082 testContext->makeCurrent();
1083 sk_sp<SkImage> refImg(imageMaker(dContext));
1084
1085 otherTestContext->makeCurrent();
1086 canvas->drawImage(refImg, 0, 0);
1087
1088 testContext->makeCurrent();
1089 refImg.reset(nullptr); // force a release of the image
1090
1091 otherTestContext->makeCurrent();
1092 // Sync is specifically here for vulkan to guarantee the command buffer will finish
1093 // which is when we call the ReleaseProc.
1094 otherCtx->flushAndSubmit(surface.get(), GrSyncCpu::kYes);
1095 }
1096
1097 // Case #6: Verify that only one context can be using the image at a time
1098 {
1099 // Suppress warnings about trying to use a texture in two contexts.
1100 GrRecordingContextPriv::AutoSuppressWarningMessages aswm(otherCtx);
1101
1102 testContext->makeCurrent();
1103 sk_sp <SkImage> refImg(imageMaker(dContext));
1104 GrSurfaceProxyView view, otherView, viewSecondRef;
1105
1106 // Any context should be able to borrow the texture at this point
1107
1108 std::tie(view, std::ignore) =
1109 skgpu::ganesh::AsView(dContext, refImg, skgpu::Mipmapped::kNo);
1110 REPORTER_ASSERT(reporter, view);
1111
1112 // But once it's borrowed, no other context should be able to borrow
1113 otherTestContext->makeCurrent();
1114 std::tie(otherView, std::ignore) =
1115 skgpu::ganesh::AsView(otherCtx, refImg, skgpu::Mipmapped::kNo);
1116 REPORTER_ASSERT(reporter, !otherView);
1117
1118 // Original context (that's already borrowing) should be okay
1119 testContext->makeCurrent();
1120 std::tie(viewSecondRef, std::ignore) =
1121 skgpu::ganesh::AsView(dContext, refImg, skgpu::Mipmapped::kNo);
1122 REPORTER_ASSERT(reporter, viewSecondRef);
1123
1124 // Release first ref from the original context
1125 view.reset();
1126
1127 // We released one proxy but not the other from the current borrowing context. Make sure
1128 // a new context is still not able to borrow the texture.
1129 otherTestContext->makeCurrent();
1130 std::tie(otherView, std::ignore) =
1131 skgpu::ganesh::AsView(otherCtx, refImg, skgpu::Mipmapped::kNo);
1132 REPORTER_ASSERT(reporter, !otherView);
1133
1134 // Release second ref from the original context
1135 testContext->makeCurrent();
1136 viewSecondRef.reset();
1137
1138 // Now we should be able to borrow the texture from the other context
1139 otherTestContext->makeCurrent();
1140 std::tie(otherView, std::ignore) =
1141 skgpu::ganesh::AsView(otherCtx, refImg, skgpu::Mipmapped::kNo);
1142 REPORTER_ASSERT(reporter, otherView);
1143
1144 // Release everything
1145 otherView.reset();
1146 refImg.reset(nullptr);
1147 }
1148 }
1149 }
1150
DEF_GANESH_TEST(SkImage_MakeCrossContextFromPixmapRelease,reporter,options,CtsEnforcement::kApiLevel_T)1151 DEF_GANESH_TEST(SkImage_MakeCrossContextFromPixmapRelease,
1152 reporter,
1153 options,
1154 CtsEnforcement::kApiLevel_T) {
1155 SkBitmap bitmap;
1156 SkPixmap pixmap;
1157 if (!ToolUtils::GetResourceAsBitmap("images/mandrill_128.png", &bitmap) ||
1158 !bitmap.peekPixels(&pixmap)) {
1159 ERRORF(reporter, "missing resource");
1160 return;
1161 }
1162 test_cross_context_image(reporter,
1163 options,
1164 "SkImage_MakeCrossContextFromPixmapRelease",
1165 [&pixmap](GrDirectContext* dContext) {
1166 return SkImages::CrossContextTextureFromPixmap(
1167 dContext, pixmap, false);
1168 });
1169 }
1170
DEF_GANESH_TEST(SkImage_CrossContextGrayAlphaConfigs,reporter,options,CtsEnforcement::kApiLevel_T)1171 DEF_GANESH_TEST(SkImage_CrossContextGrayAlphaConfigs,
1172 reporter,
1173 options,
1174 CtsEnforcement::kApiLevel_T) {
1175 for (SkColorType ct : { kGray_8_SkColorType, kAlpha_8_SkColorType }) {
1176 SkAutoPixmapStorage pixmap;
1177 pixmap.alloc(SkImageInfo::Make(4, 4, ct, kPremul_SkAlphaType));
1178
1179 for (int i = 0; i < skgpu::kContextTypeCount; ++i) {
1180 GrContextFactory testFactory(options);
1181 skgpu::ContextType ctxType = static_cast<skgpu::ContextType>(i);
1182 ContextInfo ctxInfo = testFactory.getContextInfo(ctxType);
1183 auto dContext = ctxInfo.directContext();
1184 if (!dContext || !dContext->priv().caps()->crossContextTextureSupport()) {
1185 continue;
1186 }
1187
1188 sk_sp<SkImage> image = SkImages::CrossContextTextureFromPixmap(dContext, pixmap, false);
1189 REPORTER_ASSERT(reporter, image);
1190
1191 auto [view, viewCT] = skgpu::ganesh::AsView(dContext, image, skgpu::Mipmapped::kNo);
1192 REPORTER_ASSERT(reporter, view);
1193 REPORTER_ASSERT(reporter, GrColorTypeToSkColorType(viewCT) == ct);
1194
1195 bool expectAlpha = kAlpha_8_SkColorType == ct;
1196 GrColorType grCT = SkColorTypeToGrColorType(image->colorType());
1197 REPORTER_ASSERT(reporter, expectAlpha == GrColorTypeIsAlphaOnly(grCT));
1198 }
1199 }
1200 }
1201
DEF_GANESH_TEST_FOR_GL_CONTEXT(makeBackendTexture,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)1202 DEF_GANESH_TEST_FOR_GL_CONTEXT(makeBackendTexture, reporter, ctxInfo, CtsEnforcement::kApiLevel_T) {
1203 auto context = ctxInfo.directContext();
1204 sk_gpu_test::TestContext* testContext = ctxInfo.testContext();
1205 sk_sp<GrContextThreadSafeProxy> proxy = context->threadSafeProxy();
1206
1207 GrContextFactory otherFactory;
1208 ContextInfo otherContextInfo = otherFactory.getContextInfo(ctxInfo.type());
1209
1210 testContext->makeCurrent();
1211 REPORTER_ASSERT(reporter, proxy);
1212 auto createLarge = [context] {
1213 return create_image_large(context->priv().caps()->maxTextureSize());
1214 };
1215 struct TestCase {
1216 std::function<sk_sp<SkImage>()> fImageFactory;
1217 bool fExpectation;
1218 bool fCanTakeDirectly;
1219 };
1220 TestCase testCases[] = {
1221 { create_image, true, false },
1222 { create_codec_image, true, false },
1223 { create_data_image, true, false },
1224 { create_picture_image, true, false },
1225 { [context] { return create_gpu_image(context); }, true, true },
1226 // Create a texture image in a another context.
1227 { [otherContextInfo] {
1228 auto restore = otherContextInfo.testContext()->makeCurrentAndAutoRestore();
1229 sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.directContext());
1230 otherContextInfo.directContext()->flushAndSubmit();
1231 return otherContextImage;
1232 }, false, false },
1233 // Create an image that is too large to be texture backed.
1234 { createLarge, false, false }
1235 };
1236
1237 for (const TestCase& testCase : testCases) {
1238 sk_sp<SkImage> image(testCase.fImageFactory());
1239 if (!image) {
1240 ERRORF(reporter, "Failed to create image!");
1241 continue;
1242 }
1243
1244 GrBackendTexture origBackend;
1245 SkImages::GetBackendTextureFromImage(image, &origBackend, true);
1246 if (testCase.fCanTakeDirectly) {
1247 SkASSERT(origBackend.isValid());
1248 }
1249
1250 GrBackendTexture newBackend;
1251 SkImages::BackendTextureReleaseProc proc;
1252 bool result = SkImages::MakeBackendTextureFromImage(
1253 context, std::move(image), &newBackend, &proc);
1254 if (result != testCase.fExpectation) {
1255 static const char *const kFS[] = { "fail", "succeed" };
1256 ERRORF(reporter, "This image was expected to %s but did not.",
1257 kFS[testCase.fExpectation]);
1258 }
1259
1260 if (result) {
1261 SkASSERT(newBackend.isValid());
1262 }
1263
1264 bool tookDirectly = result && GrBackendTexture::TestingOnly_Equals(origBackend, newBackend);
1265 if (testCase.fCanTakeDirectly != tookDirectly) {
1266 static const char *const kExpectedState[] = { "not expected", "expected" };
1267 ERRORF(reporter, "This backend texture was %s to be taken directly.",
1268 kExpectedState[testCase.fCanTakeDirectly]);
1269 }
1270
1271 context->flushAndSubmit();
1272 }
1273 }
1274
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageBackendAccessAbandoned_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)1275 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageBackendAccessAbandoned_Gpu,
1276 reporter,
1277 ctxInfo,
1278 CtsEnforcement::kApiLevel_T) {
1279 auto dContext = ctxInfo.directContext();
1280 sk_sp<SkImage> image(create_gpu_image(ctxInfo.directContext()));
1281 if (!image) {
1282 return;
1283 }
1284
1285 GrBackendTexture beTex;
1286 bool ok = SkImages::GetBackendTextureFromImage(image, &beTex, true);
1287 REPORTER_ASSERT(reporter, ok);
1288 REPORTER_ASSERT(reporter, beTex.isValid());
1289
1290 dContext->abandonContext();
1291
1292 // After abandoning the context the backend texture should not be valid.
1293 ok = SkImages::GetBackendTextureFromImage(image, &beTex, true);
1294 REPORTER_ASSERT(reporter, !ok);
1295 }
1296
1297 ///////////////////////////////////////////////////////////////////////////////////////////////////
1298
create_picture_image(sk_sp<SkColorSpace> space)1299 static sk_sp<SkImage> create_picture_image(sk_sp<SkColorSpace> space) {
1300 SkPictureRecorder recorder;
1301 SkCanvas* canvas = recorder.beginRecording(10, 10);
1302 canvas->clear(SK_ColorCYAN);
1303 return SkImages::DeferredFromPicture(recorder.finishRecordingAsPicture(),
1304 SkISize::Make(10, 10),
1305 nullptr,
1306 nullptr,
1307 SkImages::BitDepth::kU8,
1308 std::move(space));
1309 }
1310
DEF_TEST(Image_ColorSpace,r)1311 DEF_TEST(Image_ColorSpace, r) {
1312 sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
1313 sk_sp<SkImage> image = ToolUtils::GetResourceAsImage("images/mandrill_512_q075.jpg");
1314 REPORTER_ASSERT(r, srgb.get() == image->colorSpace());
1315
1316 image = ToolUtils::GetResourceAsImage("images/webp-color-profile-lossy.webp");
1317 skcms_TransferFunction fn;
1318 bool success = image->colorSpace()->isNumericalTransferFn(&fn);
1319 REPORTER_ASSERT(r, success);
1320 REPORTER_ASSERT(r, color_space_almost_equal(1.8f, fn.g));
1321
1322 sk_sp<SkColorSpace> rec2020 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
1323 SkNamedGamut::kRec2020);
1324 image = create_picture_image(rec2020);
1325 REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
1326
1327 SkBitmap bitmap;
1328 SkImageInfo info = SkImageInfo::MakeN32(10, 10, kPremul_SkAlphaType, rec2020);
1329 bitmap.allocPixels(info);
1330 image = bitmap.asImage();
1331 REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
1332
1333 sk_sp<SkSurface> surface =
1334 SkSurfaces::Raster(SkImageInfo::MakeN32Premul(SkISize::Make(10, 10)));
1335 image = surface->makeImageSnapshot();
1336 REPORTER_ASSERT(r, nullptr == image->colorSpace());
1337
1338 surface = SkSurfaces::Raster(info);
1339 image = surface->makeImageSnapshot();
1340 REPORTER_ASSERT(r, SkColorSpace::Equals(rec2020.get(), image->colorSpace()));
1341 }
1342
DEF_TEST(Image_makeColorSpace,r)1343 DEF_TEST(Image_makeColorSpace, r) {
1344 sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
1345 skcms_TransferFunction fn;
1346 fn.a = 1.f; fn.b = 0.f; fn.c = 0.f; fn.d = 0.f; fn.e = 0.f; fn.f = 0.f; fn.g = 1.8f;
1347 sk_sp<SkColorSpace> adobeGamut = SkColorSpace::MakeRGB(fn, SkNamedGamut::kAdobeRGB);
1348
1349 SkBitmap srgbBitmap;
1350 srgbBitmap.allocPixels(SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType));
1351 *srgbBitmap.getAddr32(0, 0) = SkSwizzle_RGBA_to_PMColor(0xFF604020);
1352 srgbBitmap.setImmutable();
1353 sk_sp<SkImage> srgbImage = srgbBitmap.asImage();
1354 sk_sp<SkImage> p3Image = srgbImage->makeColorSpace(nullptr, p3);
1355 SkBitmap p3Bitmap;
1356 bool success = p3Image->asLegacyBitmap(&p3Bitmap);
1357
1358 auto almost_equal = [](int a, int b) { return SkTAbs(a - b) <= 2; };
1359
1360 REPORTER_ASSERT(r, success);
1361 REPORTER_ASSERT(r, almost_equal(0x28, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
1362 REPORTER_ASSERT(r, almost_equal(0x40, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
1363 REPORTER_ASSERT(r, almost_equal(0x5E, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
1364
1365 sk_sp<SkImage> adobeImage = srgbImage->makeColorSpace(nullptr, adobeGamut);
1366 SkBitmap adobeBitmap;
1367 success = adobeImage->asLegacyBitmap(&adobeBitmap);
1368 REPORTER_ASSERT(r, success);
1369 REPORTER_ASSERT(r, almost_equal(0x21, SkGetPackedR32(*adobeBitmap.getAddr32(0, 0))));
1370 REPORTER_ASSERT(r, almost_equal(0x31, SkGetPackedG32(*adobeBitmap.getAddr32(0, 0))));
1371 REPORTER_ASSERT(r, almost_equal(0x4C, SkGetPackedB32(*adobeBitmap.getAddr32(0, 0))));
1372
1373 srgbImage = ToolUtils::GetResourceAsImage("images/1x1.png");
1374 p3Image = srgbImage->makeColorSpace(nullptr, p3);
1375 success = p3Image->asLegacyBitmap(&p3Bitmap);
1376 REPORTER_ASSERT(r, success);
1377 REPORTER_ASSERT(r, almost_equal(0x8B, SkGetPackedR32(*p3Bitmap.getAddr32(0, 0))));
1378 REPORTER_ASSERT(r, almost_equal(0x82, SkGetPackedG32(*p3Bitmap.getAddr32(0, 0))));
1379 REPORTER_ASSERT(r, almost_equal(0x77, SkGetPackedB32(*p3Bitmap.getAddr32(0, 0))));
1380 }
1381
1382 ///////////////////////////////////////////////////////////////////////////////////////////////////
1383
make_all_premul(SkBitmap * bm)1384 static void make_all_premul(SkBitmap* bm) {
1385 bm->allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
1386 for (int a = 0; a < 256; ++a) {
1387 for (int r = 0; r < 256; ++r) {
1388 // make all valid premul combinations
1389 int c = std::min(a, r);
1390 *bm->getAddr32(a, r) = SkPackARGB32(a, c, c, c);
1391 }
1392 }
1393 }
1394
equal(const SkBitmap & a,const SkBitmap & b)1395 static bool equal(const SkBitmap& a, const SkBitmap& b) {
1396 SkASSERT(a.width() == b.width());
1397 SkASSERT(a.height() == b.height());
1398 for (int y = 0; y < a.height(); ++y) {
1399 for (int x = 0; x < a.width(); ++x) {
1400 SkPMColor pa = *a.getAddr32(x, y);
1401 SkPMColor pb = *b.getAddr32(x, y);
1402 if (pa != pb) {
1403 return false;
1404 }
1405 }
1406 }
1407 return true;
1408 }
1409
DEF_TEST(image_roundtrip_encode,reporter)1410 DEF_TEST(image_roundtrip_encode, reporter) {
1411 SkBitmap bm0;
1412 make_all_premul(&bm0);
1413
1414 auto img0 = bm0.asImage();
1415 sk_sp<SkData> data = SkPngEncoder::Encode(nullptr, img0.get(), {});
1416 auto img1 = SkImages::DeferredFromEncodedData(data);
1417
1418 SkBitmap bm1;
1419 bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
1420 img1->readPixels(nullptr, bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0);
1421
1422 REPORTER_ASSERT(reporter, equal(bm0, bm1));
1423 }
1424
DEF_TEST(image_roundtrip_premul,reporter)1425 DEF_TEST(image_roundtrip_premul, reporter) {
1426 SkBitmap bm0;
1427 make_all_premul(&bm0);
1428
1429 SkBitmap bm1;
1430 bm1.allocPixels(SkImageInfo::MakeN32(256, 256, kUnpremul_SkAlphaType));
1431 bm0.readPixels(bm1.info(), bm1.getPixels(), bm1.rowBytes(), 0, 0);
1432
1433 SkBitmap bm2;
1434 bm2.allocPixels(SkImageInfo::MakeN32(256, 256, kPremul_SkAlphaType));
1435 bm1.readPixels(bm2.info(), bm2.getPixels(), bm2.rowBytes(), 0, 0);
1436
1437 REPORTER_ASSERT(reporter, equal(bm0, bm2));
1438 }
1439
DEF_TEST(image_from_encoded_alphatype_override,reporter)1440 DEF_TEST(image_from_encoded_alphatype_override, reporter) {
1441 sk_sp<SkData> data = GetResourceAsData("images/mandrill_32.png");
1442
1443 // Ensure that we can decode the image when we specifically request premul or unpremul, but
1444 // not when we request kOpaque
1445 REPORTER_ASSERT(reporter, SkImages::DeferredFromEncodedData(data, kPremul_SkAlphaType));
1446 REPORTER_ASSERT(reporter, SkImages::DeferredFromEncodedData(data, kUnpremul_SkAlphaType));
1447 REPORTER_ASSERT(reporter, !SkImages::DeferredFromEncodedData(data, kOpaque_SkAlphaType));
1448
1449 // Same tests as above, but using SkImageGenerators::MakeFromEncoded
1450 REPORTER_ASSERT(reporter, SkImageGenerators::MakeFromEncoded(data, kPremul_SkAlphaType));
1451 REPORTER_ASSERT(reporter, SkImageGenerators::MakeFromEncoded(data, kUnpremul_SkAlphaType));
1452 REPORTER_ASSERT(reporter, !SkImageGenerators::MakeFromEncoded(data, kOpaque_SkAlphaType));
1453 }
1454
1455 ///////////////////////////////////////////////////////////////////////////////////////////////////
1456
check_scaled_pixels(skiatest::Reporter * reporter,SkPixmap * pmap,uint32_t expected)1457 static void check_scaled_pixels(skiatest::Reporter* reporter, SkPixmap* pmap, uint32_t expected) {
1458 // Verify that all pixels contain the original test color
1459 for (auto y = 0; y < pmap->height(); ++y) {
1460 for (auto x = 0; x < pmap->width(); ++x) {
1461 uint32_t pixel = *pmap->addr32(x, y);
1462 if (pixel != expected) {
1463 ERRORF(reporter, "Expected scaled pixels to be the same. At %d,%d 0x%08x != 0x%08x",
1464 x, y, pixel, expected);
1465 return;
1466 }
1467 }
1468 }
1469 }
1470
test_scale_pixels(skiatest::Reporter * reporter,const SkImage * image,uint32_t expected)1471 static void test_scale_pixels(skiatest::Reporter* reporter, const SkImage* image,
1472 uint32_t expected) {
1473 SkImageInfo info = SkImageInfo::MakeN32Premul(image->width() * 2, image->height() * 2);
1474
1475 // Make sure to test kDisallow first, so we don't just get a cache hit in that case
1476 for (auto chint : { SkImage::kDisallow_CachingHint, SkImage::kAllow_CachingHint }) {
1477 SkAutoPixmapStorage scaled;
1478 scaled.alloc(info);
1479 if (!image->scalePixels(scaled, SkSamplingOptions(SkFilterMode::kLinear), chint)) {
1480 ERRORF(reporter, "Failed to scale image");
1481 continue;
1482 }
1483
1484 check_scaled_pixels(reporter, &scaled, expected);
1485 }
1486 }
1487
DEF_TEST(ImageScalePixels,reporter)1488 DEF_TEST(ImageScalePixels, reporter) {
1489 const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0);
1490 const SkColor red = SK_ColorRED;
1491
1492 // Test raster image
1493 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
1494 sk_sp<SkSurface> surface = SkSurfaces::Raster(info);
1495 surface->getCanvas()->clear(red);
1496 sk_sp<SkImage> rasterImage = surface->makeImageSnapshot();
1497 test_scale_pixels(reporter, rasterImage.get(), pmRed);
1498
1499 // Test encoded image
1500 sk_sp<SkData> data = SkPngEncoder::Encode(nullptr, rasterImage.get(), {});
1501 sk_sp<SkImage> codecImage = SkImages::DeferredFromEncodedData(data);
1502 test_scale_pixels(reporter, codecImage.get(), pmRed);
1503 }
1504
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageScalePixels_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)1505 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ImageScalePixels_Gpu,
1506 reporter,
1507 ctxInfo,
1508 CtsEnforcement::kApiLevel_T) {
1509 const SkPMColor pmRed = SkPackARGB32(0xFF, 0xFF, 0, 0);
1510 const SkColor red = SK_ColorRED;
1511
1512 SkImageInfo info = SkImageInfo::MakeN32Premul(16, 16);
1513 sk_sp<SkSurface> surface =
1514 SkSurfaces::RenderTarget(ctxInfo.directContext(), skgpu::Budgeted::kNo, info);
1515 surface->getCanvas()->clear(red);
1516 sk_sp<SkImage> gpuImage = surface->makeImageSnapshot();
1517 test_scale_pixels(reporter, gpuImage.get(), pmRed);
1518 }
1519
any_image_will_do()1520 static sk_sp<SkImage> any_image_will_do() {
1521 return ToolUtils::GetResourceAsImage("images/mandrill_32.png");
1522 }
1523
DEF_TEST(Image_nonfinite_dst,reporter)1524 DEF_TEST(Image_nonfinite_dst, reporter) {
1525 auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(10, 10));
1526 auto img = any_image_will_do();
1527
1528 for (SkScalar bad : { SK_ScalarInfinity, SK_ScalarNaN}) {
1529 for (int bits = 1; bits <= 15; ++bits) {
1530 SkRect dst = { 0, 0, 10, 10 };
1531 if (bits & 1) dst.fLeft = bad;
1532 if (bits & 2) dst.fTop = bad;
1533 if (bits & 4) dst.fRight = bad;
1534 if (bits & 8) dst.fBottom = bad;
1535
1536 surf->getCanvas()->drawImageRect(img, dst, SkSamplingOptions());
1537
1538 // we should draw nothing
1539 ToolUtils::PixelIter iter(surf.get());
1540 while (void* addr = iter.next()) {
1541 REPORTER_ASSERT(reporter, *(SkPMColor*)addr == 0);
1542 }
1543 }
1544 }
1545 }
1546
make_yuva_image(GrDirectContext * dContext)1547 static sk_sp<SkImage> make_yuva_image(GrDirectContext* dContext) {
1548 SkAutoPixmapStorage pm;
1549 pm.alloc(SkImageInfo::Make(1, 1, kAlpha_8_SkColorType, kPremul_SkAlphaType));
1550 SkYUVAInfo yuvaInfo({1, 1},
1551 SkYUVAInfo::PlaneConfig::kY_U_V,
1552 SkYUVAInfo::Subsampling::k444,
1553 kJPEG_Full_SkYUVColorSpace);
1554 const SkPixmap pmaps[] = {pm, pm, pm};
1555 auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(yuvaInfo, pmaps);
1556
1557 return SkImages::TextureFromYUVAPixmaps(dContext, yuvaPixmaps);
1558 }
1559
DEF_GANESH_TEST_FOR_ALL_CONTEXTS(ImageFlush,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)1560 DEF_GANESH_TEST_FOR_ALL_CONTEXTS(ImageFlush, reporter, ctxInfo, CtsEnforcement::kApiLevel_T) {
1561 auto dContext = ctxInfo.directContext();
1562 auto ii = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
1563 auto s = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kYes, ii, 1, nullptr);
1564
1565 s->getCanvas()->clear(SK_ColorRED);
1566 auto i0 = s->makeImageSnapshot();
1567 s->getCanvas()->clear(SK_ColorBLUE);
1568 auto i1 = s->makeImageSnapshot();
1569 s->getCanvas()->clear(SK_ColorGREEN);
1570 // Make a YUVA image.
1571 auto i2 = make_yuva_image(dContext);
1572
1573 // Flush all the setup work we did above and then make little lambda that reports the flush
1574 // count delta since the last time it was called.
1575 dContext->flushAndSubmit();
1576 auto numSubmits =
1577 [dContext,
1578 submitCnt = dContext->priv().getGpu()->stats()->numSubmitToGpus()]() mutable {
1579 int curr = dContext->priv().getGpu()->stats()->numSubmitToGpus();
1580 int n = curr - submitCnt;
1581 submitCnt = curr;
1582 return n;
1583 };
1584
1585 // Images aren't used therefore flush is ignored, but submit is still called.
1586 dContext->flushAndSubmit(i0);
1587 dContext->flushAndSubmit(i1);
1588 dContext->flushAndSubmit(i2);
1589 REPORTER_ASSERT(reporter, numSubmits() == 3);
1590
1591 // Syncing forces the flush to happen even if the images aren't used.
1592 dContext->flush(i0);
1593 dContext->submit(GrSyncCpu::kYes);
1594 REPORTER_ASSERT(reporter, numSubmits() == 1);
1595 dContext->flush(i1);
1596 dContext->submit(GrSyncCpu::kYes);
1597 REPORTER_ASSERT(reporter, numSubmits() == 1);
1598 dContext->flush(i2);
1599 dContext->submit(GrSyncCpu::kYes);
1600 REPORTER_ASSERT(reporter, numSubmits() == 1);
1601
1602 // Use image 1
1603 s->getCanvas()->drawImage(i1, 0, 0);
1604 // Flushing image 0 should do nothing, but submit is still called.
1605 dContext->flushAndSubmit(i0);
1606 REPORTER_ASSERT(reporter, numSubmits() == 1);
1607 // Flushing image 1 should flush.
1608 dContext->flushAndSubmit(i1);
1609 REPORTER_ASSERT(reporter, numSubmits() == 1);
1610 // Flushing image 2 should do nothing, but submit is still called.
1611 dContext->flushAndSubmit(i2);
1612 REPORTER_ASSERT(reporter, numSubmits() == 1);
1613
1614 // Use image 2
1615 s->getCanvas()->drawImage(i2, 0, 0);
1616 // Flushing image 0 should do nothing, but submit is still called.
1617 dContext->flushAndSubmit(i0);
1618 REPORTER_ASSERT(reporter, numSubmits() == 1);
1619 // Flushing image 1 do nothing, but submit is still called.
1620 dContext->flushAndSubmit(i1);
1621 REPORTER_ASSERT(reporter, numSubmits() == 1);
1622 // Flushing image 2 should flush.
1623 dContext->flushAndSubmit(i2);
1624 REPORTER_ASSERT(reporter, numSubmits() == 1);
1625 REPORTER_ASSERT(reporter, static_cast<SkImage_GaneshYUVA*>(as_IB(i2.get()))->isTextureBacked());
1626 s->getCanvas()->drawImage(i2, 0, 0);
1627 // Flushing image 0 should do nothing, but submit is still called.
1628 dContext->flushAndSubmit(i0);
1629 REPORTER_ASSERT(reporter, numSubmits() == 1);
1630 // Flushing image 1 do nothing, but submit is still called.
1631 dContext->flushAndSubmit(i1);
1632 REPORTER_ASSERT(reporter, numSubmits() == 1);
1633 // Flushing image 2 should flush.
1634 dContext->flushAndSubmit(i2);
1635 REPORTER_ASSERT(reporter, numSubmits() == 1);
1636 }
1637
1638 constexpr SkM44 gCentripetalCatmulRom
1639 (0.0f/2, -1.0f/2, 2.0f/2, -1.0f/2,
1640 2.0f/2, 0.0f/2, -5.0f/2, 3.0f/2,
1641 0.0f/2, 1.0f/2, 4.0f/2, -3.0f/2,
1642 0.0f/2, 0.0f/2, -1.0f/2, 1.0f/2);
1643
1644 constexpr SkM44 gMitchellNetravali
1645 ( 1.0f/18, -9.0f/18, 15.0f/18, -7.0f/18,
1646 16.0f/18, 0.0f/18, -36.0f/18, 21.0f/18,
1647 1.0f/18, 9.0f/18, 27.0f/18, -21.0f/18,
1648 0.0f/18, 0.0f/18, -6.0f/18, 7.0f/18);
1649
DEF_TEST(image_cubicresampler,reporter)1650 DEF_TEST(image_cubicresampler, reporter) {
1651 auto diff = [reporter](const SkM44& a, const SkM44& b) {
1652 const float tolerance = 0.000001f;
1653 for (int r = 0; r < 4; ++r) {
1654 for (int c = 0; c < 4; ++c) {
1655 float d = std::abs(a.rc(r, c) - b.rc(r, c));
1656 REPORTER_ASSERT(reporter, d <= tolerance);
1657 }
1658 }
1659 };
1660
1661 diff(SkImageShader::CubicResamplerMatrix(1.0f/3, 1.0f/3), gMitchellNetravali);
1662
1663 diff(SkImageShader::CubicResamplerMatrix(0, 1.0f/2), gCentripetalCatmulRom);
1664 }
1665
DEF_TEST(image_subset_encode_skbug_7752,reporter)1666 DEF_TEST(image_subset_encode_skbug_7752, reporter) {
1667 sk_sp<SkImage> image = ToolUtils::GetResourceAsImage("images/mandrill_128.png");
1668 const int W = image->width();
1669 const int H = image->height();
1670
1671 auto check_roundtrip = [&](const sk_sp<SkImage>& img) {
1672 auto img2 = SkImages::DeferredFromEncodedData(SkPngEncoder::Encode(nullptr, img.get(), {}));
1673 REPORTER_ASSERT(reporter, ToolUtils::equal_pixels(img.get(), img2.get()));
1674 };
1675 check_roundtrip(image); // should trivially pass
1676 check_roundtrip(image->makeSubset(nullptr, {0, 0, W/2, H/2}));
1677 check_roundtrip(image->makeSubset(nullptr, {W/2, H/2, W, H}));
1678 check_roundtrip(image->makeColorSpace(nullptr, SkColorSpace::MakeSRGBLinear()));
1679 }
1680