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 "SkImage_Base.h" 9#include "SkImageCacherator.h" 10 11#include "SkBitmap.h" 12#include "SkBitmapCache.h" 13#include "SkData.h" 14#include "SkImageGenerator.h" 15#include "SkImagePriv.h" 16#include "SkNextID.h" 17#include "SkPixelRef.h" 18 19#if SK_SUPPORT_GPU 20#include "GrContext.h" 21#include "GrContextPriv.h" 22#include "GrGpuResourcePriv.h" 23#include "GrImageTextureMaker.h" 24#include "GrResourceKey.h" 25#include "GrProxyProvider.h" 26#include "GrSamplerState.h" 27#include "GrYUVProvider.h" 28#include "SkGr.h" 29#endif 30 31// Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing one generator among N images 32class SharedGenerator final : public SkNVRefCnt<SharedGenerator> { 33public: 34 static sk_sp<SharedGenerator> Make(std::unique_ptr<SkImageGenerator> gen) { 35 return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr; 36 } 37 38 // This is thread safe. It is a const field set in the constructor. 39 const SkImageInfo& getInfo() { return fGenerator->getInfo(); } 40 41private: 42 explicit SharedGenerator(std::unique_ptr<SkImageGenerator> gen) 43 : fGenerator(std::move(gen)) { 44 SkASSERT(fGenerator); 45 } 46 47 friend class ScopedGenerator; 48 friend class SkImage_Lazy; 49 50 std::unique_ptr<SkImageGenerator> fGenerator; 51 SkMutex fMutex; 52}; 53 54class SkImage_Lazy : public SkImage_Base, public SkImageCacherator { 55public: 56 struct Validator { 57 Validator(sk_sp<SharedGenerator>, const SkIRect* subset, sk_sp<SkColorSpace> colorSpace); 58 59 operator bool() const { return fSharedGenerator.get(); } 60 61 sk_sp<SharedGenerator> fSharedGenerator; 62 SkImageInfo fInfo; 63 SkIPoint fOrigin; 64 sk_sp<SkColorSpace> fColorSpace; 65 uint32_t fUniqueID; 66 }; 67 68 SkImage_Lazy(Validator* validator); 69 70 SkImageInfo onImageInfo() const override { 71 return fInfo; 72 } 73 SkAlphaType onAlphaType() const override { 74 return fInfo.alphaType(); 75 } 76 77 bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY, 78 CachingHint) const override; 79#if SK_SUPPORT_GPU 80 sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, 81 const GrSamplerState&, SkColorSpace*, 82 sk_sp<SkColorSpace>*, 83 SkScalar scaleAdjust[2]) const override; 84#endif 85 SkData* onRefEncoded() const override; 86 sk_sp<SkImage> onMakeSubset(const SkIRect&) const override; 87 bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override; 88 bool onIsLazyGenerated() const override { return true; } 89 bool onCanLazyGenerateOnGPU() const override; 90 sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>, SkColorType, 91 SkTransferFunctionBehavior) const override; 92 93 bool onIsValid(GrContext*) const override; 94 95 SkImageCacherator* peekCacherator() const override { 96 return const_cast<SkImage_Lazy*>(this); 97 } 98 99 // Only return true if the generate has already been cached. 100 bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*, CachedFormat) const; 101 // Call the underlying generator directly 102 bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, 103 int srcX, int srcY, SkTransferFunctionBehavior behavior) const; 104 105 // SkImageCacherator interface 106#if SK_SUPPORT_GPU 107 // Returns the texture proxy. If the cacherator is generating the texture and wants to cache it, 108 // it should use the passed in key (if the key is valid). 109 sk_sp<GrTextureProxy> lockTextureProxy(GrContext*, 110 const GrUniqueKey& key, 111 SkImage::CachingHint, 112 bool willBeMipped, 113 SkColorSpace* dstColorSpace, 114 GrTextureMaker::AllowedTexGenType genType) override; 115 116 // Returns the color space of the texture that would be returned if you called lockTexture. 117 // Separate code path to allow querying of the color space for textures that cached (even 118 // externally). 119 sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace) override; 120 void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat, 121 GrUniqueKey* cacheKey) override; 122#endif 123 124 CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace, 125 const GrCaps* = nullptr) const override; 126 SkImageInfo buildCacheInfo(CachedFormat) const override; 127 128private: 129 class ScopedGenerator; 130 131 /** 132 * On success (true), bitmap will point to the pixels for this generator. If this returns 133 * false, the bitmap will be reset to empty. 134 */ 135 bool lockAsBitmap(SkBitmap*, SkImage::CachingHint, CachedFormat, const SkImageInfo&, 136 SkTransferFunctionBehavior) const; 137 138 /** 139 * Populates parameters to pass to the generator for reading pixels or generating a texture. 140 * For image generators, legacy versus true color blending is indicated using a 141 * SkTransferFunctionBehavior, and the target color space is specified on the SkImageInfo. 142 * If generatorImageInfo has no color space set, set its color space to this SkImage's color 143 * space, and return "ignore" behavior, indicating legacy mode. If generatorImageInfo has a 144 * color space set, return "respect" behavior, indicating linear blending mode. 145 */ 146 SkTransferFunctionBehavior getGeneratorBehaviorAndInfo(SkImageInfo* generatorImageInfo) const; 147 148 sk_sp<SharedGenerator> fSharedGenerator; 149 // Note that fInfo is not necessarily the info from the generator. It may be cropped by 150 // onMakeSubset and its color space may be changed by onMakeColorSpace. 151 const SkImageInfo fInfo; 152 const SkIPoint fOrigin; 153 154 struct IDRec { 155 SkOnce fOnce; 156 uint32_t fUniqueID; 157 }; 158 mutable IDRec fIDRecs[kNumCachedFormats]; 159 160 uint32_t getUniqueID(CachedFormat) const; 161 162 // Repeated calls to onMakeColorSpace will result in a proliferation of unique IDs and 163 // SkImage_Lazy instances. Cache the result of the last successful onMakeColorSpace call. 164 mutable SkMutex fOnMakeColorSpaceMutex; 165 mutable sk_sp<SkColorSpace> fOnMakeColorSpaceTarget; 166 mutable sk_sp<SkImage> fOnMakeColorSpaceResult; 167 168 typedef SkImage_Base INHERITED; 169}; 170 171/////////////////////////////////////////////////////////////////////////////// 172 173SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset, 174 sk_sp<SkColorSpace> colorSpace) 175 : fSharedGenerator(std::move(gen)) { 176 if (!fSharedGenerator) { 177 return; 178 } 179 180 // The following generator accessors are safe without acquiring the mutex (const getters). 181 // TODO: refactor to use a ScopedGenerator instead, for clarity. 182 const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo(); 183 if (info.isEmpty()) { 184 fSharedGenerator.reset(); 185 return; 186 } 187 188 fUniqueID = fSharedGenerator->fGenerator->uniqueID(); 189 const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height()); 190 if (subset) { 191 if (!bounds.contains(*subset)) { 192 fSharedGenerator.reset(); 193 return; 194 } 195 if (*subset != bounds) { 196 // we need a different uniqueID since we really are a subset of the raw generator 197 fUniqueID = SkNextID::ImageID(); 198 } 199 } else { 200 subset = &bounds; 201 } 202 203 fInfo = info.makeWH(subset->width(), subset->height()); 204 fOrigin = SkIPoint::Make(subset->x(), subset->y()); 205 if (colorSpace) { 206 fInfo = fInfo.makeColorSpace(colorSpace); 207 fUniqueID = SkNextID::ImageID(); 208 } 209} 210 211/////////////////////////////////////////////////////////////////////////////// 212 213// Helper for exclusive access to a shared generator. 214class SkImage_Lazy::ScopedGenerator { 215public: 216 ScopedGenerator(const sk_sp<SharedGenerator>& gen) 217 : fSharedGenerator(gen) 218 , fAutoAquire(gen->fMutex) {} 219 220 SkImageGenerator* operator->() const { 221 fSharedGenerator->fMutex.assertHeld(); 222 return fSharedGenerator->fGenerator.get(); 223 } 224 225 operator SkImageGenerator*() const { 226 fSharedGenerator->fMutex.assertHeld(); 227 return fSharedGenerator->fGenerator.get(); 228 } 229 230private: 231 const sk_sp<SharedGenerator>& fSharedGenerator; 232 SkAutoExclusive fAutoAquire; 233}; 234 235/////////////////////////////////////////////////////////////////////////////// 236 237SkImage_Lazy::SkImage_Lazy(Validator* validator) 238 : INHERITED(validator->fInfo.width(), validator->fInfo.height(), validator->fUniqueID) 239 , fSharedGenerator(std::move(validator->fSharedGenerator)) 240 , fInfo(validator->fInfo) 241 , fOrigin(validator->fOrigin) { 242 SkASSERT(fSharedGenerator); 243 // We explicit set the legacy format slot, but leave the others uninitialized (via SkOnce) 244 // and only resolove them to IDs as needed (by calling getUniqueID()). 245 fIDRecs[kLegacy_CachedFormat].fOnce([this, validator] { 246 fIDRecs[kLegacy_CachedFormat].fUniqueID = validator->fUniqueID; 247 }); 248} 249 250uint32_t SkImage_Lazy::getUniqueID(CachedFormat format) const { 251 IDRec* rec = &fIDRecs[format]; 252 rec->fOnce([rec] { 253 rec->fUniqueID = SkNextID::ImageID(); 254 }); 255 return rec->fUniqueID; 256} 257 258////////////////////////////////////////////////////////////////////////////////////////////////// 259 260// Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because 261// we're in raster mode), or where GPU support is entirely missing. In theory, we only need the 262// chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we 263// won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage 264// works, so we require that the formats we choose are renderable (as a proxy for being readable). 265struct CacheCaps { 266 CacheCaps(const GrCaps* caps) : fCaps(caps) {} 267 268#if SK_SUPPORT_GPU 269 bool supportsHalfFloat() const { 270 return !fCaps || (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) && 271 fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig)); 272 } 273 274 bool supportsSRGB() const { 275 return !fCaps || 276 (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig)); 277 } 278 279 bool supportsSBGR() const { 280 return !fCaps || fCaps->srgbSupport(); 281 } 282#else 283 bool supportsHalfFloat() const { return true; } 284 bool supportsSRGB() const { return true; } 285 bool supportsSBGR() const { return true; } 286#endif 287 288 const GrCaps* fCaps; 289}; 290 291SkImageCacherator::CachedFormat SkImage_Lazy::chooseCacheFormat(SkColorSpace* dstColorSpace, 292 const GrCaps* grCaps) const { 293 SkColorSpace* cs = fInfo.colorSpace(); 294 if (!cs || !dstColorSpace) { 295 return kLegacy_CachedFormat; 296 } 297 298 CacheCaps caps(grCaps); 299 switch (fInfo.colorType()) { 300 case kUnknown_SkColorType: 301 case kAlpha_8_SkColorType: 302 case kRGB_565_SkColorType: 303 case kARGB_4444_SkColorType: 304 case kRGB_888x_SkColorType: 305 case kRGBA_1010102_SkColorType: 306 case kRGB_101010x_SkColorType: 307 // We don't support color space on these formats, so always decode in legacy mode: 308 // TODO: Ask the codec to decode these to something else (at least sRGB 8888)? 309 return kLegacy_CachedFormat; 310 311 case kGray_8_SkColorType: 312 // TODO: What do we do with grayscale sources that have strange color spaces attached? 313 // The codecs and color space xform don't handle this correctly (yet), so drop it on 314 // the floor. (Also, inflating by a factor of 8 is going to be unfortunate). 315 // As it is, we don't directly support sRGB grayscale, so ask the codec to convert 316 // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture. 317 if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) { 318 return kSRGB8888_CachedFormat; 319 } else { 320 return kLegacy_CachedFormat; 321 } 322 323 case kRGBA_8888_SkColorType: 324 if (cs->gammaCloseToSRGB()) { 325 if (caps.supportsSRGB()) { 326 return kSRGB8888_CachedFormat; 327 } else if (caps.supportsHalfFloat()) { 328 return kLinearF16_CachedFormat; 329 } else { 330 return kLegacy_CachedFormat; 331 } 332 } else { 333 if (caps.supportsHalfFloat()) { 334 return kLinearF16_CachedFormat; 335 } else if (caps.supportsSRGB()) { 336 return kSRGB8888_CachedFormat; 337 } else { 338 return kLegacy_CachedFormat; 339 } 340 } 341 342 case kBGRA_8888_SkColorType: 343 // Odd case. sBGRA isn't a real thing, so we may not have this texturable. 344 if (caps.supportsSBGR()) { 345 if (cs->gammaCloseToSRGB()) { 346 return kSBGR8888_CachedFormat; 347 } else if (caps.supportsHalfFloat()) { 348 return kLinearF16_CachedFormat; 349 } else if (caps.supportsSRGB()) { 350 return kSRGB8888_CachedFormat; 351 } else { 352 // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless. 353 return kLegacy_CachedFormat; 354 } 355 } else { 356 if (cs->gammaCloseToSRGB()) { 357 if (caps.supportsSRGB()) { 358 return kSRGB8888_CachedFormat; 359 } else if (caps.supportsHalfFloat()) { 360 return kLinearF16_CachedFormat; 361 } else { 362 return kLegacy_CachedFormat; 363 } 364 } else { 365 if (caps.supportsHalfFloat()) { 366 return kLinearF16_CachedFormat; 367 } else if (caps.supportsSRGB()) { 368 return kSRGB8888_CachedFormat; 369 } else { 370 return kLegacy_CachedFormat; 371 } 372 } 373 } 374 375 case kRGBA_F16_SkColorType: 376 if (caps.supportsHalfFloat()) { 377 return kLinearF16_CachedFormat; 378 } else if (caps.supportsSRGB()) { 379 return kSRGB8888_CachedFormat; 380 } else { 381 return kLegacy_CachedFormat; 382 } 383 } 384 SkDEBUGFAIL("Unreachable"); 385 return kLegacy_CachedFormat; 386} 387 388SkImageInfo SkImage_Lazy::buildCacheInfo(CachedFormat format) const { 389 switch (format) { 390 case kLegacy_CachedFormat: 391 return fInfo.makeColorSpace(nullptr); 392 case kLinearF16_CachedFormat: 393 return fInfo.makeColorType(kRGBA_F16_SkColorType) 394 .makeColorSpace(fInfo.colorSpace()->makeLinearGamma()); 395 case kSRGB8888_CachedFormat: 396 // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec 397 // to bother trans-coding. It would be slow, and do more harm than good visually, 398 // so we make sure to leave the colorspace as-is. 399 if (fInfo.colorSpace()->gammaCloseToSRGB()) { 400 return fInfo.makeColorType(kRGBA_8888_SkColorType); 401 } else { 402 return fInfo.makeColorType(kRGBA_8888_SkColorType) 403 .makeColorSpace(fInfo.colorSpace()->makeSRGBGamma()); 404 } 405 case kSBGR8888_CachedFormat: 406 // See note above about not-quite-sRGB transfer functions. 407 if (fInfo.colorSpace()->gammaCloseToSRGB()) { 408 return fInfo.makeColorType(kBGRA_8888_SkColorType); 409 } else { 410 return fInfo.makeColorType(kBGRA_8888_SkColorType) 411 .makeColorSpace(fInfo.colorSpace()->makeSRGBGamma()); 412 } 413 default: 414 SkDEBUGFAIL("Invalid cached format"); 415 return fInfo; 416 } 417} 418 419////////////////////////////////////////////////////////////////////////////////////////////////// 420 421static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) { 422 SkASSERT(bitmap.getGenerationID() == expectedID); 423 SkASSERT(bitmap.isImmutable()); 424 SkASSERT(bitmap.getPixels()); 425 return true; 426} 427 428bool SkImage_Lazy::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb, 429 int srcX, int srcY, 430 SkTransferFunctionBehavior behavior) const { 431 ScopedGenerator generator(fSharedGenerator); 432 const SkImageInfo& genInfo = generator->getInfo(); 433 // Currently generators do not natively handle subsets, so check that first. 434 if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) { 435 return false; 436 } 437 438 SkImageGenerator::Options opts; 439 // TODO: This should respect the behavior argument. 440 opts.fBehavior = SkTransferFunctionBehavior::kIgnore; 441 return generator->getPixels(info, pixels, rb, &opts); 442} 443 444////////////////////////////////////////////////////////////////////////////////////////////////// 445 446bool SkImage_Lazy::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) const { 447 uint32_t uniqueID = this->getUniqueID(format); 448 return SkBitmapCache::Find(SkBitmapCacheDesc::Make(uniqueID, 449 fInfo.width(), fInfo.height()), bitmap) && 450 check_output_bitmap(*bitmap, uniqueID); 451} 452 453static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY, 454 SkTransferFunctionBehavior behavior) { 455 const int genW = gen->getInfo().width(); 456 const int genH = gen->getInfo().height(); 457 const SkIRect srcR = SkIRect::MakeWH(genW, genH); 458 const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height()); 459 if (!srcR.contains(dstR)) { 460 return false; 461 } 462 463 // If they are requesting a subset, we have to have a temp allocation for full image, and 464 // then copy the subset into their allocation 465 SkBitmap full; 466 SkPixmap fullPM; 467 const SkPixmap* dstPM = &pmap; 468 if (srcR != dstR) { 469 if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) { 470 return false; 471 } 472 if (!full.peekPixels(&fullPM)) { 473 return false; 474 } 475 dstPM = &fullPM; 476 } 477 478 SkImageGenerator::Options opts; 479 opts.fBehavior = behavior; 480 if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes(), &opts)) { 481 return false; 482 } 483 484 if (srcR != dstR) { 485 if (!full.readPixels(pmap, originX, originY)) { 486 return false; 487 } 488 } 489 return true; 490} 491 492bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint, CachedFormat format, 493 const SkImageInfo& info, 494 SkTransferFunctionBehavior behavior) const { 495 if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) { 496 return true; 497 } 498 499 uint32_t uniqueID = this->getUniqueID(format); 500 501 SkBitmap tmpBitmap; 502 SkBitmapCache::RecPtr cacheRec; 503 SkPixmap pmap; 504 if (SkImage::kAllow_CachingHint == chint) { 505 auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height()); 506 cacheRec = SkBitmapCache::Alloc(desc, info, &pmap); 507 if (!cacheRec) { 508 return false; 509 } 510 } else { 511 if (!tmpBitmap.tryAllocPixels(info)) { 512 return false; 513 } 514 if (!tmpBitmap.peekPixels(&pmap)) { 515 return false; 516 } 517 } 518 519 ScopedGenerator generator(fSharedGenerator); 520 if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y(), behavior)) { 521 return false; 522 } 523 524 if (cacheRec) { 525 SkBitmapCache::Add(std::move(cacheRec), bitmap); 526 SkASSERT(bitmap->getPixels()); // we're locked 527 SkASSERT(bitmap->isImmutable()); 528 SkASSERT(bitmap->getGenerationID() == uniqueID); 529 this->notifyAddedToCache(); 530 } else { 531 *bitmap = tmpBitmap; 532 bitmap->pixelRef()->setImmutableWithID(uniqueID); 533 } 534 535 check_output_bitmap(*bitmap, uniqueID); 536 return true; 537} 538 539////////////////////////////////////////////////////////////////////////////////////////////////// 540 541bool SkImage_Lazy::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, 542 int srcX, int srcY, CachingHint chint) const { 543 SkColorSpace* dstColorSpace = dstInfo.colorSpace(); 544 SkBitmap bm; 545 if (kDisallow_CachingHint == chint) { 546 CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace); 547 SkImageInfo genPixelsInfo = dstInfo; 548 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo); 549 if (this->lockAsBitmapOnlyIfAlreadyCached(&bm, cacheFormat)) { 550 return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY); 551 } else { 552 // Try passing the caller's buffer directly down to the generator. If this fails we 553 // may still succeed in the general case, as the generator may prefer some other 554 // config, which we could then convert via SkBitmap::readPixels. 555 if (this->directGeneratePixels(genPixelsInfo, dstPixels, dstRB, srcX, srcY, behavior)) { 556 return true; 557 } 558 // else fall through 559 } 560 } 561 562 if (this->getROPixels(&bm, dstColorSpace, chint)) { 563 return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY); 564 } 565 return false; 566} 567 568SkData* SkImage_Lazy::onRefEncoded() const { 569 ScopedGenerator generator(fSharedGenerator); 570 return generator->refEncodedData(); 571} 572 573bool SkImage_Lazy::getROPixels(SkBitmap* bitmap, SkColorSpace* dstColorSpace, 574 CachingHint chint) const { 575 CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace); 576 const SkImageInfo cacheInfo = this->buildCacheInfo(cacheFormat); 577 SkImageInfo genPixelsInfo = cacheInfo; 578 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo); 579 return this->lockAsBitmap(bitmap, chint, cacheFormat, genPixelsInfo, behavior); 580} 581 582bool SkImage_Lazy::onIsValid(GrContext* context) const { 583 ScopedGenerator generator(fSharedGenerator); 584 return generator->isValid(context); 585} 586 587bool SkImage_Lazy::onCanLazyGenerateOnGPU() const { 588#if SK_SUPPORT_GPU 589 ScopedGenerator generator(fSharedGenerator); 590 return SkImageGenerator::TexGenType::kNone != generator->onCanGenerateTexture(); 591#else 592 return false; 593#endif 594} 595 596SkTransferFunctionBehavior SkImage_Lazy::getGeneratorBehaviorAndInfo(SkImageInfo* generatorImageInfo) const { 597 if (generatorImageInfo->colorSpace()) { 598 return SkTransferFunctionBehavior::kRespect; 599 } 600 // Only specify an output color space if color conversion can be done on the color type. 601 switch (generatorImageInfo->colorType()) { 602 case kRGBA_8888_SkColorType: 603 case kBGRA_8888_SkColorType: 604 case kRGBA_F16_SkColorType: 605 case kRGB_565_SkColorType: 606 *generatorImageInfo = generatorImageInfo->makeColorSpace(fInfo.refColorSpace()); 607 break; 608 default: 609 break; 610 } 611 return SkTransferFunctionBehavior::kIgnore; 612} 613 614/////////////////////////////////////////////////////////////////////////////////////////////////// 615 616#if SK_SUPPORT_GPU 617sk_sp<GrTextureProxy> SkImage_Lazy::asTextureProxyRef(GrContext* context, 618 const GrSamplerState& params, 619 SkColorSpace* dstColorSpace, 620 sk_sp<SkColorSpace>* texColorSpace, 621 SkScalar scaleAdjust[2]) const { 622 if (!context) { 623 return nullptr; 624 } 625 626 GrImageTextureMaker textureMaker(context, this, kAllow_CachingHint); 627 return textureMaker.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust); 628} 629#endif 630 631sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset) const { 632 SkASSERT(fInfo.bounds().contains(subset)); 633 SkASSERT(fInfo.bounds() != subset); 634 635 const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y()); 636 Validator validator(fSharedGenerator, &generatorSubset, fInfo.refColorSpace()); 637 return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr; 638} 639 640sk_sp<SkImage> SkImage_Lazy::onMakeColorSpace(sk_sp<SkColorSpace> target, 641 SkColorType targetColorType, 642 SkTransferFunctionBehavior premulBehavior) const { 643 SkAutoExclusive autoAquire(fOnMakeColorSpaceMutex); 644 if (target && fOnMakeColorSpaceTarget && 645 SkColorSpace::Equals(target.get(), fOnMakeColorSpaceTarget.get())) { 646 return fOnMakeColorSpaceResult; 647 } 648 const SkIRect generatorSubset = 649 SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height()); 650 Validator validator(fSharedGenerator, &generatorSubset, target); 651 sk_sp<SkImage> result = validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr; 652 if (result) { 653 fOnMakeColorSpaceTarget = target; 654 fOnMakeColorSpaceResult = result; 655 } 656 return result; 657} 658 659sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator, 660 const SkIRect* subset) { 661 SkImage_Lazy::Validator validator(SharedGenerator::Make(std::move(generator)), subset, nullptr); 662 663 return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr; 664} 665 666////////////////////////////////////////////////////////////////////////////////////////////////// 667 668/** 669 * Implementation of SkImageCacherator interface, as needed by GrImageTextureMaker 670 */ 671 672#if SK_SUPPORT_GPU 673 674void SkImage_Lazy::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format, 675 GrUniqueKey* cacheKey) { 676 SkASSERT(!cacheKey->isValid()); 677 if (origKey.isValid()) { 678 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 679 GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1, "Image"); 680 builder[0] = format; 681 } 682} 683 684class Generator_GrYUVProvider : public GrYUVProvider { 685 SkImageGenerator* fGen; 686 687public: 688 Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {} 689 690 uint32_t onGetID() override { return fGen->uniqueID(); } 691 bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override { 692 return fGen->queryYUV8(sizeInfo, colorSpace); 693 } 694 bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override { 695 return fGen->getYUV8Planes(sizeInfo, planes); 696 } 697}; 698 699static void set_key_on_proxy(GrProxyProvider* proxyProvider, 700 GrTextureProxy* proxy, GrTextureProxy* originalProxy, 701 const GrUniqueKey& key) { 702 if (key.isValid()) { 703 SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); 704 if (originalProxy && originalProxy->getUniqueKey().isValid()) { 705 SkASSERT(originalProxy->getUniqueKey() == key); 706 SkASSERT(GrMipMapped::kYes == proxy->mipMapped() && 707 GrMipMapped::kNo == originalProxy->mipMapped()); 708 // If we had an originalProxy with a valid key, that means there already is a proxy in 709 // the cache which matches the key, but it does not have mip levels and we require them. 710 // Thus we must remove the unique key from that proxy. 711 proxyProvider->removeUniqueKeyFromProxy(key, originalProxy); 712 } 713 proxyProvider->assignUniqueKeyToProxy(key, proxy); 714 } 715} 716 717sk_sp<SkColorSpace> SkImage_Lazy::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) { 718 if (!dstColorSpace) { 719 // In legacy mode, we do no modification to the image's color space or encoding. 720 // Subsequent legacy drawing is likely to ignore the color space, but some clients 721 // may want to know what space the image data is in, so return it. 722 return fInfo.refColorSpace(); 723 } else { 724 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps()); 725 SkImageInfo cacheInfo = this->buildCacheInfo(format); 726 return cacheInfo.refColorSpace(); 727 } 728} 729 730/* 731 * We have 4 ways to try to return a texture (in sorted order) 732 * 733 * 1. Check the cache for a pre-existing one 734 * 2. Ask the generator to natively create one 735 * 3. Ask the generator to return YUV planes, which the GPU can convert 736 * 4. Ask the generator to return RGB(A) data, which the GPU can convert 737 */ 738sk_sp<GrTextureProxy> SkImage_Lazy::lockTextureProxy(GrContext* ctx, 739 const GrUniqueKey& origKey, 740 SkImage::CachingHint chint, 741 bool willBeMipped, 742 SkColorSpace* dstColorSpace, 743 GrTextureMaker::AllowedTexGenType genType) { 744 // Values representing the various texture lock paths we can take. Used for logging the path 745 // taken to a histogram. 746 enum LockTexturePath { 747 kFailure_LockTexturePath, 748 kPreExisting_LockTexturePath, 749 kNative_LockTexturePath, 750 kCompressed_LockTexturePath, // Deprecated 751 kYUV_LockTexturePath, 752 kRGBA_LockTexturePath, 753 }; 754 755 enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 }; 756 757 // Determine which cached format we're going to use (which may involve decoding to a different 758 // info than the generator provides). 759 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps()); 760 761 // Fold the cache format into our texture key 762 GrUniqueKey key; 763 this->makeCacheKeyFromOrigKey(origKey, format, &key); 764 765 GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider(); 766 sk_sp<GrTextureProxy> proxy; 767 768 // 1. Check the cache for a pre-existing one 769 if (key.isValid()) { 770 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin); 771 if (proxy) { 772 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath, 773 kLockTexturePathCount); 774 if (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped()) { 775 return proxy; 776 } 777 } 778 } 779 780 // The CachedFormat is both an index for which cache "slot" we'll use to store this particular 781 // decoded variant of the encoded data, and also a recipe for how to transform the original 782 // info to get the one that we're going to decode to. 783 const SkImageInfo cacheInfo = this->buildCacheInfo(format); 784 SkImageInfo genPixelsInfo = cacheInfo; 785 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo); 786 787 // 2. Ask the generator to natively create one 788 if (!proxy) { 789 ScopedGenerator generator(fSharedGenerator); 790 if (GrTextureMaker::AllowedTexGenType::kCheap == genType && 791 SkImageGenerator::TexGenType::kCheap != generator->onCanGenerateTexture()) { 792 return nullptr; 793 } 794 if ((proxy = generator->generateTexture(ctx, genPixelsInfo, fOrigin, behavior, 795 willBeMipped))) { 796 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath, 797 kLockTexturePathCount); 798 set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key); 799 if (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped()) { 800 return proxy; 801 } 802 } 803 } 804 805 // 3. Ask the generator to return YUV planes, which the GPU can convert. If we will be mipping 806 // the texture we fall through here and have the CPU generate the mip maps for us. 807 if (!proxy && !willBeMipped && !ctx->contextPriv().disableGpuYUVConversion()) { 808 const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps()); 809 ScopedGenerator generator(fSharedGenerator); 810 Generator_GrYUVProvider provider(generator); 811 812 // The pixels in the texture will be in the generator's color space. If onMakeColorSpace 813 // has been called then this will not match this image's color space. To correct this, apply 814 // a color space conversion from the generator's color space to this image's color space. 815 const SkColorSpace* generatorColorSpace = 816 fSharedGenerator->fGenerator->getInfo().colorSpace(); 817 const SkColorSpace* thisColorSpace = fInfo.colorSpace(); 818 819 // TODO: Update to create the mipped surface in the YUV generator and draw the base layer 820 // directly into the mipped surface. 821 proxy = provider.refAsTextureProxy(ctx, desc, generatorColorSpace, thisColorSpace); 822 if (proxy) { 823 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath, 824 kLockTexturePathCount); 825 set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key); 826 return proxy; 827 } 828 } 829 830 // 4. Ask the generator to return RGB(A) data, which the GPU can convert 831 SkBitmap bitmap; 832 if (!proxy && this->lockAsBitmap(&bitmap, chint, format, genPixelsInfo, behavior)) { 833 if (willBeMipped) { 834 proxy = proxyProvider->createMipMapProxyFromBitmap(bitmap, dstColorSpace); 835 } 836 if (!proxy) { 837 proxy = GrUploadBitmapToTextureProxy(proxyProvider, bitmap, dstColorSpace); 838 } 839 if (proxy && (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped())) { 840 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath, 841 kLockTexturePathCount); 842 set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key); 843 return proxy; 844 } 845 } 846 847 if (proxy) { 848 // We need a mipped proxy, but we either found a proxy earlier that wasn't mipped, generated 849 // a native non mipped proxy, or generated a non-mipped yuv proxy. Thus we generate a new 850 // mipped surface and copy the original proxy into the base layer. We will then let the gpu 851 // generate the rest of the mips. 852 SkASSERT(willBeMipped); 853 SkASSERT(GrMipMapped::kNo == proxy->mipMapped()); 854 if (auto mippedProxy = GrCopyBaseMipMapToTextureProxy(ctx, proxy.get())) { 855 set_key_on_proxy(proxyProvider, mippedProxy.get(), proxy.get(), key); 856 return mippedProxy; 857 } 858 // We failed to make a mipped proxy with the base copied into it. This could have 859 // been from failure to make the proxy or failure to do the copy. Thus we will fall 860 // back to just using the non mipped proxy; See skbug.com/7094. 861 return proxy; 862 } 863 864 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath, 865 kLockTexturePathCount); 866 return nullptr; 867} 868 869/////////////////////////////////////////////////////////////////////////////////////////////////// 870 871#endif 872