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 "SkColorSpace_Base.h" 14 #include "SkData.h" 15 #include "SkImageGenerator.h" 16 #include "SkImagePriv.h" 17 #include "SkNextID.h" 18 #include "SkPixelRef.h" 19 20 #if SK_SUPPORT_GPU 21 #include "GrContext.h" 22 #include "GrContextPriv.h" 23 #include "GrGpuResourcePriv.h" 24 #include "GrImageTextureMaker.h" 25 #include "GrResourceKey.h" 26 #include "GrResourceProvider.h" 27 #include "GrSamplerParams.h" 28 #include "GrYUVProvider.h" 29 #include "SkGr.h" 30 #endif 31 32 // Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing one generator among N images 33 class SharedGenerator final : public SkNVRefCnt<SharedGenerator> { 34 public: 35 static sk_sp<SharedGenerator> Make(std::unique_ptr<SkImageGenerator> gen) { 36 return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr; 37 } 38 39 // This is thread safe. It is a const field set in the constructor. 40 const SkImageInfo& getInfo() { return fGenerator->getInfo(); } 41 42 private: 43 explicit SharedGenerator(std::unique_ptr<SkImageGenerator> gen) 44 : fGenerator(std::move(gen)) { 45 SkASSERT(fGenerator); 46 } 47 48 friend class ScopedGenerator; 49 friend class SkImage_Lazy; 50 51 std::unique_ptr<SkImageGenerator> fGenerator; 52 SkMutex fMutex; 53 }; 54 55 class SkImage_Lazy : public SkImage_Base, public SkImageCacherator { 56 public: 57 struct Validator { 58 Validator(sk_sp<SharedGenerator>, const SkIRect* subset, sk_sp<SkColorSpace> colorSpace); 59 60 operator bool() const { return fSharedGenerator.get(); } 61 62 sk_sp<SharedGenerator> fSharedGenerator; 63 SkImageInfo fInfo; 64 SkIPoint fOrigin; 65 sk_sp<SkColorSpace> fColorSpace; 66 uint32_t fUniqueID; 67 }; 68 69 SkImage_Lazy(Validator* validator); 70 71 SkImageInfo onImageInfo() const override { 72 return fInfo; 73 } 74 SkAlphaType onAlphaType() const override { 75 return fInfo.alphaType(); 76 } 77 78 bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY, 79 CachingHint) const override; 80 #if SK_SUPPORT_GPU 81 sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerParams&, 82 SkColorSpace*, 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 128 private: 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 173 SkImage_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. 214 class SkImage_Lazy::ScopedGenerator { 215 public: 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 230 private: 231 const sk_sp<SharedGenerator>& fSharedGenerator; 232 SkAutoExclusive fAutoAquire; 233 }; 234 235 /////////////////////////////////////////////////////////////////////////////// 236 237 SkImage_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 250 uint32_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). 265 struct CacheCaps { 266 CacheCaps(const GrCaps* caps) : fCaps(caps) {} 267 268 #if SK_SUPPORT_GPU 269 bool supportsHalfFloat() const { 270 return !fCaps || 271 (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) && 272 fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false)); 273 } 274 275 bool supportsSRGB() const { 276 return !fCaps || 277 (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig)); 278 } 279 280 bool supportsSBGR() const { 281 return !fCaps || fCaps->srgbSupport(); 282 } 283 #else 284 bool supportsHalfFloat() const { return true; } 285 bool supportsSRGB() const { return true; } 286 bool supportsSBGR() const { return true; } 287 #endif 288 289 const GrCaps* fCaps; 290 }; 291 292 SkImageCacherator::CachedFormat SkImage_Lazy::chooseCacheFormat(SkColorSpace* dstColorSpace, 293 const GrCaps* grCaps) const { 294 SkColorSpace* cs = fInfo.colorSpace(); 295 if (!cs || !dstColorSpace) { 296 return kLegacy_CachedFormat; 297 } 298 299 CacheCaps caps(grCaps); 300 switch (fInfo.colorType()) { 301 case kUnknown_SkColorType: 302 case kAlpha_8_SkColorType: 303 case kRGB_565_SkColorType: 304 case kARGB_4444_SkColorType: 305 // We don't support color space on these formats, so always decode in legacy mode: 306 // TODO: Ask the codec to decode these to something else (at least sRGB 8888)? 307 return kLegacy_CachedFormat; 308 309 case kGray_8_SkColorType: 310 // TODO: What do we do with grayscale sources that have strange color spaces attached? 311 // The codecs and color space xform don't handle this correctly (yet), so drop it on 312 // the floor. (Also, inflating by a factor of 8 is going to be unfortunate). 313 // As it is, we don't directly support sRGB grayscale, so ask the codec to convert 314 // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture. 315 if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) { 316 return kSRGB8888_CachedFormat; 317 } else { 318 return kLegacy_CachedFormat; 319 } 320 321 case kRGBA_8888_SkColorType: 322 if (cs->gammaCloseToSRGB()) { 323 if (caps.supportsSRGB()) { 324 return kSRGB8888_CachedFormat; 325 } else if (caps.supportsHalfFloat()) { 326 return kLinearF16_CachedFormat; 327 } else { 328 return kLegacy_CachedFormat; 329 } 330 } else { 331 if (caps.supportsHalfFloat()) { 332 return kLinearF16_CachedFormat; 333 } else if (caps.supportsSRGB()) { 334 return kSRGB8888_CachedFormat; 335 } else { 336 return kLegacy_CachedFormat; 337 } 338 } 339 340 case kBGRA_8888_SkColorType: 341 // Odd case. sBGRA isn't a real thing, so we may not have this texturable. 342 if (caps.supportsSBGR()) { 343 if (cs->gammaCloseToSRGB()) { 344 return kSBGR8888_CachedFormat; 345 } else if (caps.supportsHalfFloat()) { 346 return kLinearF16_CachedFormat; 347 } else if (caps.supportsSRGB()) { 348 return kSRGB8888_CachedFormat; 349 } else { 350 // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless. 351 return kLegacy_CachedFormat; 352 } 353 } else { 354 if (cs->gammaCloseToSRGB()) { 355 if (caps.supportsSRGB()) { 356 return kSRGB8888_CachedFormat; 357 } else if (caps.supportsHalfFloat()) { 358 return kLinearF16_CachedFormat; 359 } else { 360 return kLegacy_CachedFormat; 361 } 362 } else { 363 if (caps.supportsHalfFloat()) { 364 return kLinearF16_CachedFormat; 365 } else if (caps.supportsSRGB()) { 366 return kSRGB8888_CachedFormat; 367 } else { 368 return kLegacy_CachedFormat; 369 } 370 } 371 } 372 373 case kRGBA_F16_SkColorType: 374 if (caps.supportsHalfFloat()) { 375 return kLinearF16_CachedFormat; 376 } else if (caps.supportsSRGB()) { 377 return kSRGB8888_CachedFormat; 378 } else { 379 return kLegacy_CachedFormat; 380 } 381 } 382 SkDEBUGFAIL("Unreachable"); 383 return kLegacy_CachedFormat; 384 } 385 386 SkImageInfo SkImage_Lazy::buildCacheInfo(CachedFormat format) const { 387 switch (format) { 388 case kLegacy_CachedFormat: 389 return fInfo.makeColorSpace(nullptr); 390 case kLinearF16_CachedFormat: 391 return fInfo.makeColorType(kRGBA_F16_SkColorType) 392 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma()); 393 case kSRGB8888_CachedFormat: 394 // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec 395 // to bother trans-coding. It would be slow, and do more harm than good visually, 396 // so we make sure to leave the colorspace as-is. 397 if (fInfo.colorSpace()->gammaCloseToSRGB()) { 398 return fInfo.makeColorType(kRGBA_8888_SkColorType); 399 } else { 400 return fInfo.makeColorType(kRGBA_8888_SkColorType) 401 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma()); 402 } 403 case kSBGR8888_CachedFormat: 404 // See note above about not-quite-sRGB transfer functions. 405 if (fInfo.colorSpace()->gammaCloseToSRGB()) { 406 return fInfo.makeColorType(kBGRA_8888_SkColorType); 407 } else { 408 return fInfo.makeColorType(kBGRA_8888_SkColorType) 409 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma()); 410 } 411 default: 412 SkDEBUGFAIL("Invalid cached format"); 413 return fInfo; 414 } 415 } 416 417 ////////////////////////////////////////////////////////////////////////////////////////////////// 418 419 static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) { 420 SkASSERT(bitmap.getGenerationID() == expectedID); 421 SkASSERT(bitmap.isImmutable()); 422 SkASSERT(bitmap.getPixels()); 423 return true; 424 } 425 426 bool SkImage_Lazy::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb, 427 int srcX, int srcY, 428 SkTransferFunctionBehavior behavior) const { 429 ScopedGenerator generator(fSharedGenerator); 430 const SkImageInfo& genInfo = generator->getInfo(); 431 // Currently generators do not natively handle subsets, so check that first. 432 if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) { 433 return false; 434 } 435 436 SkImageGenerator::Options opts; 437 // TODO: This should respect the behavior argument. 438 opts.fBehavior = SkTransferFunctionBehavior::kIgnore; 439 return generator->getPixels(info, pixels, rb, &opts); 440 } 441 442 ////////////////////////////////////////////////////////////////////////////////////////////////// 443 444 bool SkImage_Lazy::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) const { 445 uint32_t uniqueID = this->getUniqueID(format); 446 return SkBitmapCache::Find(SkBitmapCacheDesc::Make(uniqueID, 447 fInfo.width(), fInfo.height()), bitmap) && 448 check_output_bitmap(*bitmap, uniqueID); 449 } 450 451 static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY, 452 SkTransferFunctionBehavior behavior) { 453 const int genW = gen->getInfo().width(); 454 const int genH = gen->getInfo().height(); 455 const SkIRect srcR = SkIRect::MakeWH(genW, genH); 456 const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height()); 457 if (!srcR.contains(dstR)) { 458 return false; 459 } 460 461 // If they are requesting a subset, we have to have a temp allocation for full image, and 462 // then copy the subset into their allocation 463 SkBitmap full; 464 SkPixmap fullPM; 465 const SkPixmap* dstPM = &pmap; 466 if (srcR != dstR) { 467 if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) { 468 return false; 469 } 470 if (!full.peekPixels(&fullPM)) { 471 return false; 472 } 473 dstPM = &fullPM; 474 } 475 476 SkImageGenerator::Options opts; 477 opts.fBehavior = behavior; 478 if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes(), &opts)) { 479 return false; 480 } 481 482 if (srcR != dstR) { 483 if (!full.readPixels(pmap, originX, originY)) { 484 return false; 485 } 486 } 487 return true; 488 } 489 490 bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint, CachedFormat format, 491 const SkImageInfo& info, 492 SkTransferFunctionBehavior behavior) const { 493 if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) { 494 return true; 495 } 496 497 uint32_t uniqueID = this->getUniqueID(format); 498 499 SkBitmap tmpBitmap; 500 SkBitmapCache::RecPtr cacheRec; 501 SkPixmap pmap; 502 if (SkImage::kAllow_CachingHint == chint) { 503 auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height()); 504 cacheRec = SkBitmapCache::Alloc(desc, info, &pmap); 505 if (!cacheRec) { 506 return false; 507 } 508 } else { 509 if (!tmpBitmap.tryAllocPixels(info)) { 510 return false; 511 } 512 if (!tmpBitmap.peekPixels(&pmap)) { 513 return false; 514 } 515 } 516 517 ScopedGenerator generator(fSharedGenerator); 518 if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y(), behavior)) { 519 return false; 520 } 521 522 if (cacheRec) { 523 SkBitmapCache::Add(std::move(cacheRec), bitmap); 524 SkASSERT(bitmap->getPixels()); // we're locked 525 SkASSERT(bitmap->isImmutable()); 526 SkASSERT(bitmap->getGenerationID() == uniqueID); 527 this->notifyAddedToCache(); 528 } else { 529 *bitmap = tmpBitmap; 530 bitmap->pixelRef()->setImmutableWithID(uniqueID); 531 } 532 533 check_output_bitmap(*bitmap, uniqueID); 534 return true; 535 } 536 537 ////////////////////////////////////////////////////////////////////////////////////////////////// 538 539 bool SkImage_Lazy::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB, 540 int srcX, int srcY, CachingHint chint) const { 541 SkColorSpace* dstColorSpace = dstInfo.colorSpace(); 542 SkBitmap bm; 543 if (kDisallow_CachingHint == chint) { 544 CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace); 545 SkImageInfo genPixelsInfo = dstInfo; 546 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo); 547 if (this->lockAsBitmapOnlyIfAlreadyCached(&bm, cacheFormat)) { 548 return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY); 549 } else { 550 // Try passing the caller's buffer directly down to the generator. If this fails we 551 // may still succeed in the general case, as the generator may prefer some other 552 // config, which we could then convert via SkBitmap::readPixels. 553 if (this->directGeneratePixels(genPixelsInfo, dstPixels, dstRB, srcX, srcY, behavior)) { 554 return true; 555 } 556 // else fall through 557 } 558 } 559 560 if (this->getROPixels(&bm, dstColorSpace, chint)) { 561 return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY); 562 } 563 return false; 564 } 565 566 SkData* SkImage_Lazy::onRefEncoded() const { 567 ScopedGenerator generator(fSharedGenerator); 568 return generator->refEncodedData(); 569 } 570 571 bool SkImage_Lazy::getROPixels(SkBitmap* bitmap, SkColorSpace* dstColorSpace, 572 CachingHint chint) const { 573 CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace); 574 const SkImageInfo cacheInfo = this->buildCacheInfo(cacheFormat); 575 SkImageInfo genPixelsInfo = cacheInfo; 576 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo); 577 return this->lockAsBitmap(bitmap, chint, cacheFormat, genPixelsInfo, behavior); 578 } 579 580 bool SkImage_Lazy::onIsValid(GrContext* context) const { 581 ScopedGenerator generator(fSharedGenerator); 582 return generator->isValid(context); 583 } 584 585 bool SkImage_Lazy::onCanLazyGenerateOnGPU() const { 586 #if SK_SUPPORT_GPU 587 ScopedGenerator generator(fSharedGenerator); 588 return SkImageGenerator::TexGenType::kNone != generator->onCanGenerateTexture(); 589 #else 590 return false; 591 #endif 592 } 593 594 SkTransferFunctionBehavior SkImage_Lazy::getGeneratorBehaviorAndInfo(SkImageInfo* generatorImageInfo) const { 595 if (generatorImageInfo->colorSpace()) { 596 return SkTransferFunctionBehavior::kRespect; 597 } 598 // Only specify an output color space if color conversion can be done on the color type. 599 switch (generatorImageInfo->colorType()) { 600 case kRGBA_8888_SkColorType: 601 case kBGRA_8888_SkColorType: 602 case kRGBA_F16_SkColorType: 603 case kRGB_565_SkColorType: 604 *generatorImageInfo = generatorImageInfo->makeColorSpace(fInfo.refColorSpace()); 605 break; 606 default: 607 break; 608 } 609 return SkTransferFunctionBehavior::kIgnore; 610 } 611 612 /////////////////////////////////////////////////////////////////////////////////////////////////// 613 614 #if SK_SUPPORT_GPU 615 sk_sp<GrTextureProxy> SkImage_Lazy::asTextureProxyRef(GrContext* context, 616 const GrSamplerParams& params, 617 SkColorSpace* dstColorSpace, 618 sk_sp<SkColorSpace>* texColorSpace, 619 SkScalar scaleAdjust[2]) const { 620 if (!context) { 621 return nullptr; 622 } 623 624 GrImageTextureMaker textureMaker(context, this, kAllow_CachingHint); 625 return textureMaker.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust); 626 } 627 #endif 628 629 sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset) const { 630 SkASSERT(fInfo.bounds().contains(subset)); 631 SkASSERT(fInfo.bounds() != subset); 632 633 const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y()); 634 Validator validator(fSharedGenerator, &generatorSubset, fInfo.refColorSpace()); 635 return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr; 636 } 637 638 sk_sp<SkImage> SkImage_Lazy::onMakeColorSpace(sk_sp<SkColorSpace> target, 639 SkColorType targetColorType, 640 SkTransferFunctionBehavior premulBehavior) const { 641 SkAutoExclusive autoAquire(fOnMakeColorSpaceMutex); 642 if (target && fOnMakeColorSpaceTarget && 643 SkColorSpace::Equals(target.get(), fOnMakeColorSpaceTarget.get())) { 644 return fOnMakeColorSpaceResult; 645 } 646 const SkIRect generatorSubset = 647 SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height()); 648 Validator validator(fSharedGenerator, &generatorSubset, target); 649 sk_sp<SkImage> result = validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr; 650 if (result) { 651 fOnMakeColorSpaceTarget = target; 652 fOnMakeColorSpaceResult = result; 653 } 654 return result; 655 } 656 657 sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator, 658 const SkIRect* subset) { 659 SkImage_Lazy::Validator validator(SharedGenerator::Make(std::move(generator)), subset, nullptr); 660 661 return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr; 662 } 663 664 ////////////////////////////////////////////////////////////////////////////////////////////////// 665 666 /** 667 * Implementation of SkImageCacherator interface, as needed by GrImageTextureMaker 668 */ 669 670 #if SK_SUPPORT_GPU 671 672 void SkImage_Lazy::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format, 673 GrUniqueKey* cacheKey) { 674 SkASSERT(!cacheKey->isValid()); 675 if (origKey.isValid()) { 676 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 677 GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1); 678 builder[0] = format; 679 } 680 } 681 682 class Generator_GrYUVProvider : public GrYUVProvider { 683 SkImageGenerator* fGen; 684 685 public: 686 Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {} 687 688 uint32_t onGetID() override { return fGen->uniqueID(); } 689 bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override { 690 return fGen->queryYUV8(sizeInfo, colorSpace); 691 } 692 bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override { 693 return fGen->getYUV8Planes(sizeInfo, planes); 694 } 695 }; 696 697 static void set_key_on_proxy(GrResourceProvider* resourceProvider, 698 GrTextureProxy* proxy, const GrUniqueKey& key) { 699 if (key.isValid()) { 700 resourceProvider->assignUniqueKeyToProxy(key, proxy); 701 } 702 } 703 704 sk_sp<SkColorSpace> SkImage_Lazy::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) { 705 // TODO: This isn't always correct. Picture generator currently produces textures in N32, 706 // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that 707 // information in/on the key so we can return the correct space in case #1 of lockTexture. 708 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps()); 709 SkImageInfo cacheInfo = this->buildCacheInfo(format); 710 return sk_ref_sp(cacheInfo.colorSpace()); 711 } 712 713 /* 714 * We have 4 ways to try to return a texture (in sorted order) 715 * 716 * 1. Check the cache for a pre-existing one 717 * 2. Ask the generator to natively create one 718 * 3. Ask the generator to return YUV planes, which the GPU can convert 719 * 4. Ask the generator to return RGB(A) data, which the GPU can convert 720 */ 721 sk_sp<GrTextureProxy> SkImage_Lazy::lockTextureProxy(GrContext* ctx, 722 const GrUniqueKey& origKey, 723 SkImage::CachingHint chint, 724 bool willBeMipped, 725 SkColorSpace* dstColorSpace, 726 GrTextureMaker::AllowedTexGenType genType) { 727 // Values representing the various texture lock paths we can take. Used for logging the path 728 // taken to a histogram. 729 enum LockTexturePath { 730 kFailure_LockTexturePath, 731 kPreExisting_LockTexturePath, 732 kNative_LockTexturePath, 733 kCompressed_LockTexturePath, // Deprecated 734 kYUV_LockTexturePath, 735 kRGBA_LockTexturePath, 736 }; 737 738 enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 }; 739 740 // Determine which cached format we're going to use (which may involve decoding to a different 741 // info than the generator provides). 742 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps()); 743 744 // Fold the cache format into our texture key 745 GrUniqueKey key; 746 this->makeCacheKeyFromOrigKey(origKey, format, &key); 747 748 // 1. Check the cache for a pre-existing one 749 if (key.isValid()) { 750 if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) { 751 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath, 752 kLockTexturePathCount); 753 return proxy; 754 } 755 } 756 757 // The CachedFormat is both an index for which cache "slot" we'll use to store this particular 758 // decoded variant of the encoded data, and also a recipe for how to transform the original 759 // info to get the one that we're going to decode to. 760 const SkImageInfo cacheInfo = this->buildCacheInfo(format); 761 SkImageInfo genPixelsInfo = cacheInfo; 762 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo); 763 764 // 2. Ask the generator to natively create one 765 { 766 ScopedGenerator generator(fSharedGenerator); 767 if (GrTextureMaker::AllowedTexGenType::kCheap == genType && 768 SkImageGenerator::TexGenType::kCheap != generator->onCanGenerateTexture()) { 769 return nullptr; 770 } 771 if (sk_sp<GrTextureProxy> proxy = 772 generator->generateTexture(ctx, genPixelsInfo, fOrigin, behavior)) { 773 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath, 774 kLockTexturePathCount); 775 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key); 776 return proxy; 777 } 778 } 779 780 // 3. Ask the generator to return YUV planes, which the GPU can convert 781 if (!ctx->contextPriv().disableGpuYUVConversion()) { 782 const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps()); 783 ScopedGenerator generator(fSharedGenerator); 784 Generator_GrYUVProvider provider(generator); 785 786 // The pixels in the texture will be in the generator's color space. If onMakeColorSpace 787 // has been called then this will not match this image's color space. To correct this, apply 788 // a color space conversion from the generator's color space to this image's color space. 789 const SkColorSpace* generatorColorSpace = 790 fSharedGenerator->fGenerator->getInfo().colorSpace(); 791 const SkColorSpace* thisColorSpace = fInfo.colorSpace(); 792 793 sk_sp<GrTextureProxy> proxy = 794 provider.refAsTextureProxy(ctx, desc, true, generatorColorSpace, thisColorSpace); 795 if (proxy) { 796 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath, 797 kLockTexturePathCount); 798 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key); 799 return proxy; 800 } 801 } 802 803 // 4. Ask the generator to return RGB(A) data, which the GPU can convert 804 SkBitmap bitmap; 805 if (this->lockAsBitmap(&bitmap, chint, format, genPixelsInfo, behavior)) { 806 sk_sp<GrTextureProxy> proxy; 807 if (willBeMipped) { 808 proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace); 809 } 810 if (!proxy) { 811 proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap, dstColorSpace); 812 } 813 if (proxy) { 814 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath, 815 kLockTexturePathCount); 816 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key); 817 return proxy; 818 } 819 } 820 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath, 821 kLockTexturePathCount); 822 return nullptr; 823 } 824 825 /////////////////////////////////////////////////////////////////////////////////////////////////// 826 827 #endif 828