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