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 "SkPictureShader.h" 9 10 #include "SkArenaAlloc.h" 11 #include "SkBitmap.h" 12 #include "SkBitmapProcShader.h" 13 #include "SkCanvas.h" 14 #include "SkColorSpaceXformCanvas.h" 15 #include "SkImage.h" 16 #include "SkImageShader.h" 17 #include "SkMatrixUtils.h" 18 #include "SkPicturePriv.h" 19 #include "SkReadBuffer.h" 20 #include "SkResourceCache.h" 21 #include <atomic> 22 23 #if SK_SUPPORT_GPU 24 #include "GrCaps.h" 25 #include "GrColorSpaceInfo.h" 26 #include "GrContext.h" 27 #include "GrContextPriv.h" 28 #include "GrFragmentProcessor.h" 29 #include "SkGr.h" 30 #endif 31 32 namespace { 33 static unsigned gBitmapShaderKeyNamespaceLabel; 34 35 struct BitmapShaderKey : public SkResourceCache::Key { 36 public: 37 BitmapShaderKey(SkColorSpace* colorSpace, 38 SkImage::BitDepth bitDepth, 39 uint32_t shaderID, 40 const SkSize& scale) 41 : fColorSpaceXYZHash(colorSpace->toXYZD50Hash()) 42 , fColorSpaceTransferFnHash(colorSpace->transferFnHash()) 43 , fBitDepth(bitDepth) 44 , fScale(scale) { 45 46 static const size_t keySize = sizeof(fColorSpaceXYZHash) + 47 sizeof(fColorSpaceTransferFnHash) + 48 sizeof(fBitDepth) + 49 sizeof(fScale); 50 // This better be packed. 51 SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize); 52 this->init(&gBitmapShaderKeyNamespaceLabel, MakeSharedID(shaderID), keySize); 53 } 54 55 static uint64_t MakeSharedID(uint32_t shaderID) { 56 uint64_t sharedID = SkSetFourByteTag('p', 's', 'd', 'r'); 57 return (sharedID << 32) | shaderID; 58 } 59 60 private: 61 uint32_t fColorSpaceXYZHash; 62 uint32_t fColorSpaceTransferFnHash; 63 SkImage::BitDepth fBitDepth; 64 SkSize fScale; 65 66 SkDEBUGCODE(uint32_t fEndOfStruct;) 67 }; 68 69 struct BitmapShaderRec : public SkResourceCache::Rec { 70 BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader) 71 : fKey(key) 72 , fShader(SkRef(tileShader)) {} 73 74 BitmapShaderKey fKey; 75 sk_sp<SkShader> fShader; 76 77 const Key& getKey() const override { return fKey; } 78 size_t bytesUsed() const override { 79 // Just the record overhead -- the actual pixels are accounted by SkImage_Lazy. 80 return sizeof(fKey) + sizeof(SkImageShader); 81 } 82 const char* getCategory() const override { return "bitmap-shader"; } 83 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; } 84 85 static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) { 86 const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec); 87 sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader); 88 89 *result = rec.fShader; 90 91 // The bitmap shader is backed by an image generator, thus it can always re-generate its 92 // pixels if discarded. 93 return true; 94 } 95 }; 96 97 uint32_t next_id() { 98 static std::atomic<uint32_t> nextID{1}; 99 100 uint32_t id; 101 do { 102 id = nextID++; 103 } while (id == SK_InvalidGenID); 104 return id; 105 } 106 107 } // namespace 108 109 SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy, 110 const SkMatrix* localMatrix, const SkRect* tile, 111 sk_sp<SkColorSpace> colorSpace) 112 : INHERITED(localMatrix) 113 , fPicture(std::move(picture)) 114 , fTile(tile ? *tile : fPicture->cullRect()) 115 , fTmx(tmx) 116 , fTmy(tmy) 117 , fColorSpace(std::move(colorSpace)) 118 , fUniqueID(next_id()) 119 , fAddedToCache(false) {} 120 121 SkPictureShader::~SkPictureShader() { 122 if (fAddedToCache.load()) { 123 SkResourceCache::PostPurgeSharedID(BitmapShaderKey::MakeSharedID(fUniqueID)); 124 } 125 } 126 127 sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy, 128 const SkMatrix* localMatrix, const SkRect* tile) { 129 if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) { 130 return SkShader::MakeEmptyShader(); 131 } 132 return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile, 133 nullptr)); 134 } 135 136 sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) { 137 SkMatrix lm; 138 buffer.readMatrix(&lm); 139 TileMode mx = (TileMode)buffer.read32(); 140 TileMode my = (TileMode)buffer.read32(); 141 SkRect tile; 142 buffer.readRect(&tile); 143 144 sk_sp<SkPicture> picture; 145 146 bool didSerialize = buffer.readBool(); 147 if (didSerialize) { 148 picture = SkPicturePriv::MakeFromBuffer(buffer); 149 } 150 return SkPictureShader::Make(picture, mx, my, &lm, &tile); 151 } 152 153 void SkPictureShader::flatten(SkWriteBuffer& buffer) const { 154 buffer.writeMatrix(this->getLocalMatrix()); 155 buffer.write32(fTmx); 156 buffer.write32(fTmy); 157 buffer.writeRect(fTile); 158 159 buffer.writeBool(true); 160 SkPicturePriv::Flatten(fPicture, buffer); 161 } 162 163 // Returns a cached image shader, which wraps a single picture tile at the given 164 // CTM/local matrix. Also adjusts the local matrix for tile scaling. 165 sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, 166 SkTCopyOnFirstWrite<SkMatrix>* localMatrix, 167 SkColorType dstColorType, 168 SkColorSpace* dstColorSpace, 169 const int maxTextureSize) const { 170 SkASSERT(fPicture && !fPicture->cullRect().isEmpty()); 171 172 const SkMatrix m = SkMatrix::Concat(viewMatrix, **localMatrix); 173 174 // Use a rotation-invariant scale 175 SkPoint scale; 176 // 177 // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines! 178 // 179 if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) { 180 // Decomposition failed, use an approximation. 181 scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()), 182 SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY())); 183 } 184 SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()), 185 SkScalarAbs(scale.y() * fTile.height())); 186 187 // Clamp the tile size to about 4M pixels 188 static const SkScalar kMaxTileArea = 2048 * 2048; 189 SkScalar tileArea = scaledSize.width() * scaledSize.height(); 190 if (tileArea > kMaxTileArea) { 191 SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea); 192 scaledSize.set(scaledSize.width() * clampScale, 193 scaledSize.height() * clampScale); 194 } 195 #if SK_SUPPORT_GPU 196 // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture 197 if (maxTextureSize) { 198 if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) { 199 SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height()); 200 scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale), 201 SkScalarFloorToScalar(scaledSize.height() * downScale)); 202 } 203 } 204 #endif 205 206 const SkISize tileSize = scaledSize.toCeil(); 207 if (tileSize.isEmpty()) { 208 return SkShader::MakeEmptyShader(); 209 } 210 211 // The actual scale, compensating for rounding & clamping. 212 const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(), 213 SkIntToScalar(tileSize.height()) / fTile.height()); 214 215 // |fColorSpace| will only be set when using an SkColorSpaceXformCanvas to do pre-draw xforms. 216 // A non-null |dstColorSpace| indicates that the surface we're drawing to is tagged. In all 217 // cases, picture-backed images behave the same (using a tagged surface for rasterization), 218 // and (as sources) they require a valid color space, so default to sRGB. 219 220 // With SkColorSpaceXformCanvas, the surface should never have a color space attached. 221 SkASSERT(!fColorSpace || !dstColorSpace); 222 223 sk_sp<SkColorSpace> imgCS = dstColorSpace ? sk_ref_sp(dstColorSpace) 224 : fColorSpace ? fColorSpace 225 : SkColorSpace::MakeSRGB(); 226 SkImage::BitDepth bitDepth = 227 kRGBA_F16_SkColorType == dstColorType || kRGBA_F32_SkColorType == dstColorType 228 ? SkImage::BitDepth::kF16 : SkImage::BitDepth::kU8; 229 230 BitmapShaderKey key(imgCS.get(), bitDepth, fUniqueID, tileScale); 231 232 sk_sp<SkShader> tileShader; 233 if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) { 234 SkMatrix tileMatrix; 235 tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()), 236 SkMatrix::kFill_ScaleToFit); 237 238 sk_sp<SkImage> tileImage = SkImage::MakeFromPicture(fPicture, tileSize, &tileMatrix, 239 nullptr, bitDepth, std::move(imgCS)); 240 if (!tileImage) { 241 return nullptr; 242 } 243 244 tileShader = tileImage->makeShader(fTmx, fTmy); 245 246 SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get())); 247 fAddedToCache.store(true); 248 } 249 250 if (tileScale.width() != 1 || tileScale.height() != 1) { 251 localMatrix->writable()->preScale(1 / tileScale.width(), 1 / tileScale.height()); 252 } 253 254 return tileShader; 255 } 256 257 bool SkPictureShader::onAppendStages(const StageRec& rec) const { 258 auto lm = this->totalLocalMatrix(rec.fLocalM); 259 260 // Keep bitmapShader alive by using alloc instead of stack memory 261 auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>(); 262 bitmapShader = this->refBitmapShader(rec.fCTM, &lm, rec.fDstColorType, rec.fDstCS); 263 264 if (!bitmapShader) { 265 return false; 266 } 267 268 StageRec localRec = rec; 269 localRec.fLocalM = lm->isIdentity() ? nullptr : lm.get(); 270 271 return as_SB(bitmapShader)->appendStages(localRec); 272 } 273 274 ///////////////////////////////////////////////////////////////////////////////////////// 275 276 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT 277 SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) 278 const { 279 auto lm = this->totalLocalMatrix(rec.fLocalMatrix); 280 sk_sp<SkShader> bitmapShader = this->refBitmapShader(*rec.fMatrix, &lm, rec.fDstColorType, 281 rec.fDstColorSpace); 282 if (!bitmapShader) { 283 return nullptr; 284 } 285 286 ContextRec localRec = rec; 287 localRec.fLocalMatrix = lm->isIdentity() ? nullptr : lm.get(); 288 289 PictureShaderContext* ctx = 290 alloc->make<PictureShaderContext>(*this, localRec, std::move(bitmapShader), alloc); 291 if (nullptr == ctx->fBitmapShaderContext) { 292 ctx = nullptr; 293 } 294 return ctx; 295 } 296 #endif 297 298 sk_sp<SkShader> SkPictureShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { 299 sk_sp<SkColorSpace> dstCS = xformer->dst(); 300 if (SkColorSpace::Equals(dstCS.get(), fColorSpace.get())) { 301 return sk_ref_sp(const_cast<SkPictureShader*>(this)); 302 } 303 304 return sk_sp<SkPictureShader>(new SkPictureShader(fPicture, fTmx, fTmy, &this->getLocalMatrix(), 305 &fTile, std::move(dstCS))); 306 } 307 308 ///////////////////////////////////////////////////////////////////////////////////////// 309 310 SkPictureShader::PictureShaderContext::PictureShaderContext( 311 const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader, 312 SkArenaAlloc* alloc) 313 : INHERITED(shader, rec) 314 , fBitmapShader(std::move(bitmapShader)) 315 { 316 fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc); 317 //if fBitmapShaderContext is null, we are invalid 318 } 319 320 uint32_t SkPictureShader::PictureShaderContext::getFlags() const { 321 SkASSERT(fBitmapShaderContext); 322 return fBitmapShaderContext->getFlags(); 323 } 324 325 void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { 326 SkASSERT(fBitmapShaderContext); 327 fBitmapShaderContext->shadeSpan(x, y, dstC, count); 328 } 329 330 #if SK_SUPPORT_GPU 331 std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor( 332 const GrFPArgs& args) const { 333 int maxTextureSize = 0; 334 if (args.fContext) { 335 maxTextureSize = args.fContext->contextPriv().caps()->maxTextureSize(); 336 } 337 338 auto lm = this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix); 339 SkColorType dstColorType = kN32_SkColorType; 340 GrPixelConfigToColorType(args.fDstColorSpaceInfo->config(), &dstColorType); 341 sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, &lm, dstColorType, 342 args.fDstColorSpaceInfo->colorSpace(), 343 maxTextureSize)); 344 if (!bitmapShader) { 345 return nullptr; 346 } 347 348 // We want to *reset* args.fPreLocalMatrix, not compose it. 349 GrFPArgs newArgs(args.fContext, args.fViewMatrix, args.fFilterQuality, args.fDstColorSpaceInfo); 350 newArgs.fPreLocalMatrix = lm.get(); 351 352 return as_SB(bitmapShader)->asFragmentProcessor(newArgs); 353 } 354 #endif 355