1/* 2 * Copyright 2014 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 "src/shaders/SkPictureShader.h" 9 10#include "include/core/SkBitmap.h" 11#include "include/core/SkCanvas.h" 12#include "include/core/SkImage.h" 13#include "include/private/SkImageInfoPriv.h" 14#include "src/core/SkArenaAlloc.h" 15#include "src/core/SkImagePriv.h" 16#include "src/core/SkMatrixProvider.h" 17#include "src/core/SkMatrixUtils.h" 18#include "src/core/SkPicturePriv.h" 19#include "src/core/SkReadBuffer.h" 20#include "src/core/SkResourceCache.h" 21#include "src/core/SkVM.h" 22#include "src/shaders/SkBitmapProcShader.h" 23#include "src/shaders/SkImageShader.h" 24#include <atomic> 25 26#if SK_SUPPORT_GPU 27#include "include/gpu/GrDirectContext.h" 28#include "include/gpu/GrRecordingContext.h" 29#include "src/gpu/GrCaps.h" 30#include "src/gpu/GrColorInfo.h" 31#include "src/gpu/GrFragmentProcessor.h" 32#include "src/gpu/GrRecordingContextPriv.h" 33#include "src/gpu/SkGr.h" 34#include "src/gpu/effects/GrTextureEffect.h" 35#include "src/image/SkImage_Base.h" 36#endif 37 38sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter, 39 const SkMatrix* localMatrix, const SkRect* tile) const { 40 if (localMatrix && !localMatrix->invert(nullptr)) { 41 return nullptr; 42 } 43 return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, filter, localMatrix, tile); 44} 45 46namespace { 47static unsigned gImageFromPictureKeyNamespaceLabel; 48 49struct ImageFromPictureKey : public SkResourceCache::Key { 50public: 51 ImageFromPictureKey(SkColorSpace* colorSpace, SkColorType colorType, 52 uint32_t pictureID, const SkRect& subset, 53 SkSize scale) 54 : fColorSpaceXYZHash(colorSpace->toXYZD50Hash()) 55 , fColorSpaceTransferFnHash(colorSpace->transferFnHash()) 56 , fColorType(static_cast<uint32_t>(colorType)) 57 , fSubset(subset) 58 , fScale(scale) 59 { 60 static const size_t keySize = sizeof(fColorSpaceXYZHash) + 61 sizeof(fColorSpaceTransferFnHash) + 62 sizeof(fColorType) + 63 sizeof(fSubset) + 64 sizeof(fScale); 65 // This better be packed. 66 SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize); 67 this->init(&gImageFromPictureKeyNamespaceLabel, 68 SkPicturePriv::MakeSharedID(pictureID), 69 keySize); 70 } 71 72private: 73 uint32_t fColorSpaceXYZHash; 74 uint32_t fColorSpaceTransferFnHash; 75 uint32_t fColorType; 76 SkRect fSubset; 77 SkSize fScale; 78 79 SkDEBUGCODE(uint32_t fEndOfStruct;) 80}; 81 82struct ImageFromPictureRec : public SkResourceCache::Rec { 83 ImageFromPictureRec(const ImageFromPictureKey& key, sk_sp<SkImage> image) 84 : fKey(key) 85 , fImage(std::move(image)) {} 86 87 ImageFromPictureKey fKey; 88 sk_sp<SkImage> fImage; 89 90 const Key& getKey() const override { return fKey; } 91 size_t bytesUsed() const override { 92 // Just the record overhead -- the actual pixels are accounted by SkImage_Lazy. 93 return sizeof(fKey) + (size_t)fImage->width() * fImage->height() * 4; 94 } 95 const char* getCategory() const override { return "bitmap-shader"; } 96 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; } 97 98 static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) { 99 const ImageFromPictureRec& rec = static_cast<const ImageFromPictureRec&>(baseRec); 100 sk_sp<SkImage>* result = reinterpret_cast<sk_sp<SkImage>*>(contextShader); 101 102 *result = rec.fImage; 103 return true; 104 } 105}; 106 107} // namespace 108 109SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy, 110 SkFilterMode filter, const SkMatrix* localMatrix, const SkRect* tile) 111 : INHERITED(localMatrix) 112 , fPicture(std::move(picture)) 113 , fTile(tile ? *tile : fPicture->cullRect()) 114 , fTmx(tmx) 115 , fTmy(tmy) 116 , fFilter(filter) {} 117 118sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy, 119 SkFilterMode filter, const SkMatrix* lm, const SkRect* tile) { 120 if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) { 121 return SkShaders::Empty(); 122 } 123 return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, filter, lm, tile)); 124} 125 126sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) { 127 SkMatrix lm; 128 buffer.readMatrix(&lm); 129 auto tmx = buffer.read32LE(SkTileMode::kLastTileMode); 130 auto tmy = buffer.read32LE(SkTileMode::kLastTileMode); 131 SkRect tile = buffer.readRect(); 132 133 sk_sp<SkPicture> picture; 134 135 SkFilterMode filter = SkFilterMode::kNearest; 136 if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version)) { 137 if (buffer.isVersionLT(SkPicturePriv::kPictureShaderFilterParam_Version)) { 138 bool didSerialize = buffer.readBool(); 139 if (didSerialize) { 140 picture = SkPicturePriv::MakeFromBuffer(buffer); 141 } 142 } else { 143 unsigned legacyFilter = buffer.read32(); 144 if (legacyFilter <= (unsigned)SkFilterMode::kLast) { 145 filter = (SkFilterMode)legacyFilter; 146 } 147 picture = SkPicturePriv::MakeFromBuffer(buffer); 148 } 149 } else { 150 filter = buffer.read32LE(SkFilterMode::kLast); 151 picture = SkPicturePriv::MakeFromBuffer(buffer); 152 } 153 return SkPictureShader::Make(picture, tmx, tmy, filter, &lm, &tile); 154} 155 156void SkPictureShader::flatten(SkWriteBuffer& buffer) const { 157 buffer.writeMatrix(this->getLocalMatrix()); 158 buffer.write32((unsigned)fTmx); 159 buffer.write32((unsigned)fTmy); 160 buffer.writeRect(fTile); 161 buffer.write32((unsigned)fFilter); 162 SkPicturePriv::Flatten(fPicture, buffer); 163} 164 165static sk_sp<SkColorSpace> ref_or_srgb(SkColorSpace* cs) { 166 return cs ? sk_ref_sp(cs) : SkColorSpace::MakeSRGB(); 167} 168 169struct CachedImageInfo { 170 bool success; 171 SkSize tileScale; 172 SkMatrix matrixForDraw; 173 SkImageInfo imageInfo; 174 175 static CachedImageInfo Make(const SkRect& bounds, 176 const SkMatrix& viewMatrix, 177 SkTCopyOnFirstWrite<SkMatrix>* localMatrix, // in/out 178 SkColorType dstColorType, 179 SkColorSpace* dstColorSpace, 180 const int maxTextureSize) { 181 const SkMatrix m = SkMatrix::Concat(viewMatrix, **localMatrix); 182 183 const SkSize scaledSize = [&]() { 184 SkSize size; 185 // Use a rotation-invariant scale 186 if (!m.decomposeScale(&size, nullptr)) { 187 size = {1, 1}; 188 } 189 size.fWidth *= bounds.width(); 190 size.fHeight *= bounds.height(); 191 192 // Clamp the tile size to about 4M pixels 193 static const SkScalar kMaxTileArea = 2048 * 2048; 194 SkScalar tileArea = size.width() * size.height(); 195 if (tileArea > kMaxTileArea) { 196 SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea); 197 size.set(size.width() * clampScale, size.height() * clampScale); 198 } 199 200 // Scale down the tile size if larger than maxTextureSize for GPU Path 201 // or it should fail on create texture 202 if (maxTextureSize) { 203 if (size.width() > maxTextureSize || size.height() > maxTextureSize) { 204 SkScalar downScale = maxTextureSize / std::max(size.width(), 205 size.height()); 206 size.set(SkScalarFloorToScalar(size.width() * downScale), 207 SkScalarFloorToScalar(size.height() * downScale)); 208 } 209 } 210 return size; 211 }(); 212 213 const SkISize tileSize = scaledSize.toCeil(); 214 if (tileSize.isEmpty()) { 215 return {false, {}, {}, {}}; 216 } 217 218 const SkSize tileScale = { 219 tileSize.width() / bounds.width(), tileSize.height() / bounds.height() 220 }; 221 auto imgCS = ref_or_srgb(dstColorSpace); 222 const SkColorType imgCT = SkColorTypeMaxBitsPerChannel(dstColorType) <= 8 223 ? kRGBA_8888_SkColorType 224 : kRGBA_F16Norm_SkColorType; 225 226 if (tileScale.width() != 1 || tileScale.height() != 1) { 227 localMatrix->writable()->preScale(1 / tileScale.width(), 1 / tileScale.height()); 228 } 229 230 return { 231 true, 232 tileScale, 233 SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())), 234 SkImageInfo::Make(tileSize.width(), tileSize.height(), 235 imgCT, kPremul_SkAlphaType, imgCS), 236 }; 237 } 238 239 sk_sp<SkImage> makeImage(sk_sp<SkSurface> surf, const SkPicture* pict) const { 240 if (!surf) { 241 return nullptr; 242 } 243 auto canvas = surf->getCanvas(); 244 canvas->concat(matrixForDraw); 245 canvas->drawPicture(pict); 246 return surf->makeImageSnapshot(); 247 } 248}; 249 250// Returns a cached image shader, which wraps a single picture tile at the given 251// CTM/local matrix. Also adjusts the local matrix for tile scaling. 252sk_sp<SkShader> SkPictureShader::rasterShader(const SkMatrix& viewMatrix, 253 SkTCopyOnFirstWrite<SkMatrix>* localMatrix, 254 SkColorType dstColorType, 255 SkColorSpace* dstColorSpace) const { 256 const int maxTextureSize_NotUsedForCPU = 0; 257 CachedImageInfo info = CachedImageInfo::Make(fTile, viewMatrix, localMatrix, 258 dstColorType, dstColorSpace, 259 maxTextureSize_NotUsedForCPU); 260 if (!info.success) { 261 return nullptr; 262 } 263 264 ImageFromPictureKey key(info.imageInfo.colorSpace(), info.imageInfo.colorType(), 265 fPicture->uniqueID(), fTile, info.tileScale); 266 267 sk_sp<SkImage> image; 268 if (!SkResourceCache::Find(key, ImageFromPictureRec::Visitor, &image)) { 269 image = info.makeImage(SkSurface::MakeRaster(info.imageInfo), fPicture.get()); 270 if (!image) { 271 return nullptr; 272 } 273 274 SkResourceCache::Add(new ImageFromPictureRec(key, image)); 275 SkPicturePriv::AddedToCache(fPicture.get()); 276 } 277 return image->makeShader(fTmx, fTmy, SkSamplingOptions(fFilter), nullptr); 278} 279 280bool SkPictureShader::onAppendStages(const SkStageRec& rec) const { 281 auto lm = this->totalLocalMatrix(rec.fLocalM); 282 // Keep bitmapShader alive by using alloc instead of stack memory 283 auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>(); 284 bitmapShader = this->rasterShader(rec.fMatrixProvider.localToDevice(), &lm, 285 rec.fDstColorType, rec.fDstCS); 286 if (!bitmapShader) { 287 return false; 288 } 289 290 SkStageRec localRec = rec; 291 localRec.fLocalM = lm->isIdentity() ? nullptr : lm.get(); 292 293 return as_SB(bitmapShader)->appendStages(localRec); 294} 295 296skvm::Color SkPictureShader::onProgram(skvm::Builder* p, 297 skvm::Coord device, skvm::Coord local, skvm::Color paint, 298 const SkMatrixProvider& matrices, const SkMatrix* localM, 299 const SkColorInfo& dst, 300 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const { 301 auto lm = this->totalLocalMatrix(localM); 302 303 // Keep bitmapShader alive by using alloc instead of stack memory 304 auto& bitmapShader = *alloc->make<sk_sp<SkShader>>(); 305 bitmapShader = this->rasterShader(matrices.localToDevice(), &lm, 306 dst.colorType(), dst.colorSpace()); 307 if (!bitmapShader) { 308 return {}; 309 } 310 311 return as_SB(bitmapShader)->program(p, device,local, paint, 312 matrices,lm, dst, 313 uniforms,alloc); 314} 315 316///////////////////////////////////////////////////////////////////////////////////////// 317 318#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT 319SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) 320const { 321 auto lm = this->totalLocalMatrix(rec.fLocalMatrix); 322 sk_sp<SkShader> bitmapShader = this->rasterShader(*rec.fMatrix, &lm, rec.fDstColorType, 323 rec.fDstColorSpace); 324 if (!bitmapShader) { 325 return nullptr; 326 } 327 328 ContextRec localRec = rec; 329 localRec.fLocalMatrix = lm->isIdentity() ? nullptr : lm.get(); 330 331 PictureShaderContext* ctx = 332 alloc->make<PictureShaderContext>(*this, localRec, std::move(bitmapShader), alloc); 333 if (nullptr == ctx->fBitmapShaderContext) { 334 ctx = nullptr; 335 } 336 return ctx; 337} 338#endif 339 340///////////////////////////////////////////////////////////////////////////////////////// 341 342SkPictureShader::PictureShaderContext::PictureShaderContext( 343 const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader, 344 SkArenaAlloc* alloc) 345 : INHERITED(shader, rec) 346 , fBitmapShader(std::move(bitmapShader)) 347{ 348 fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc); 349 //if fBitmapShaderContext is null, we are invalid 350} 351 352uint32_t SkPictureShader::PictureShaderContext::getFlags() const { 353 SkASSERT(fBitmapShaderContext); 354 return fBitmapShaderContext->getFlags(); 355} 356 357void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { 358 SkASSERT(fBitmapShaderContext); 359 fBitmapShaderContext->shadeSpan(x, y, dstC, count); 360} 361 362#if SK_SUPPORT_GPU 363 364#include "src/gpu/GrProxyProvider.h" 365 366std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor( 367 const GrFPArgs& args) const { 368 369 auto ctx = args.fContext; 370 auto lm = this->totalLocalMatrix(args.fPreLocalMatrix); 371 SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType()); 372 if (dstColorType == kUnknown_SkColorType) { 373 dstColorType = kRGBA_8888_SkColorType; 374 } 375 376 auto dstCS = ref_or_srgb(args.fDstColorInfo->colorSpace()); 377 auto info = CachedImageInfo::Make(fTile, args.fMatrixProvider.localToDevice(), &lm, 378 dstColorType, dstCS.get(), 379 ctx->priv().caps()->maxTextureSize()); 380 SkMatrix inv; 381 if (!info.success || !(*lm).invert(&inv)) { 382 return nullptr; 383 } 384 385 // Gotta be sure the GPU can support our requested colortype (might be FP16) 386 if (!ctx->colorTypeSupportedAsSurface(info.imageInfo.colorType())) { 387 info.imageInfo = info.imageInfo.makeColorType(kRGBA_8888_SkColorType); 388 } 389 390 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 391 GrUniqueKey key; 392 GrUniqueKey::Builder builder(&key, kDomain, 10, "Picture Shader Image"); 393 builder[0] = dstCS->toXYZD50Hash(); 394 builder[1] = dstCS->transferFnHash(); 395 builder[2] = static_cast<uint32_t>(dstColorType); 396 builder[3] = fPicture->uniqueID(); 397 memcpy(&builder[4], &fTile, sizeof(fTile)); // 4,5,6,7 398 memcpy(&builder[8], &info.tileScale, sizeof(info.tileScale)); // 8,9 399 builder.finish(); 400 401 GrProxyProvider* provider = ctx->priv().proxyProvider(); 402 GrSurfaceProxyView view; 403 if (auto proxy = provider->findOrCreateProxyByUniqueKey(key)) { 404 view = GrSurfaceProxyView(proxy, kTopLeft_GrSurfaceOrigin, GrSwizzle()); 405 } else { 406 const int msaaSampleCount = 0; 407 const SkSurfaceProps* props = nullptr; 408 const bool createWithMips = false; 409 auto image = info.makeImage(SkSurface::MakeRenderTarget(ctx, 410 SkBudgeted::kYes, 411 info.imageInfo, 412 msaaSampleCount, 413 kTopLeft_GrSurfaceOrigin, 414 props, 415 createWithMips), 416 fPicture.get()); 417 if (!image) { 418 return nullptr; 419 } 420 auto [v, ct] = as_IB(image)->asView(ctx, GrMipmapped::kNo); 421 view = std::move(v); 422 provider->assignUniqueKeyToProxy(key, view.asTextureProxy()); 423 } 424 425 const GrSamplerState sampler(static_cast<GrSamplerState::WrapMode>(fTmx), 426 static_cast<GrSamplerState::WrapMode>(fTmy), 427 fFilter); 428 429 return GrTextureEffect::Make( 430 std::move(view), kPremul_SkAlphaType, inv, sampler, *ctx->priv().caps()); 431} 432#endif 433