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 "SkBitmap.h" 11 #include "SkBitmapProcShader.h" 12 #include "SkCanvas.h" 13 #include "SkMatrixUtils.h" 14 #include "SkPicture.h" 15 #include "SkReadBuffer.h" 16 17 #if SK_SUPPORT_GPU 18 #include "GrContext.h" 19 #endif 20 21 SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy, 22 const SkMatrix* localMatrix, const SkRect* tile) 23 : INHERITED(localMatrix) 24 , fPicture(SkRef(picture)) 25 , fTile(tile ? *tile : picture->cullRect()) 26 , fTmx(tmx) 27 , fTmy(tmy) { 28 } 29 30 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING 31 SkPictureShader::SkPictureShader(SkReadBuffer& buffer) : INHERITED(buffer) { 32 fTmx = static_cast<SkShader::TileMode>(buffer.read32()); 33 fTmy = static_cast<SkShader::TileMode>(buffer.read32()); 34 buffer.readRect(&fTile); 35 fPicture = SkPicture::CreateFromBuffer(buffer); 36 } 37 #endif 38 39 SkPictureShader::~SkPictureShader() { 40 fPicture->unref(); 41 } 42 43 SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, TileMode tmy, 44 const SkMatrix* localMatrix, const SkRect* tile) { 45 if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) { 46 return NULL; 47 } 48 return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix, tile)); 49 } 50 51 SkFlattenable* SkPictureShader::CreateProc(SkReadBuffer& buffer) { 52 SkMatrix lm; 53 buffer.readMatrix(&lm); 54 TileMode mx = (TileMode)buffer.read32(); 55 TileMode my = (TileMode)buffer.read32(); 56 SkRect tile; 57 buffer.readRect(&tile); 58 SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromBuffer(buffer)); 59 return SkPictureShader::Create(picture, mx, my, &lm, &tile); 60 } 61 62 void SkPictureShader::flatten(SkWriteBuffer& buffer) const { 63 buffer.writeMatrix(this->getLocalMatrix()); 64 buffer.write32(fTmx); 65 buffer.write32(fTmy); 66 buffer.writeRect(fTile); 67 fPicture->flatten(buffer); 68 } 69 70 SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const { 71 SkASSERT(fPicture && !fPicture->cullRect().isEmpty()); 72 73 SkMatrix m; 74 m.setConcat(matrix, this->getLocalMatrix()); 75 if (localM) { 76 m.preConcat(*localM); 77 } 78 79 // Use a rotation-invariant scale 80 SkPoint scale; 81 if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) { 82 // Decomposition failed, use an approximation. 83 scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()), 84 SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY())); 85 } 86 SkSize scaledSize = SkSize::Make(scale.x() * fTile.width(), scale.y() * fTile.height()); 87 88 // Clamp the tile size to about 16M pixels 89 static const SkScalar kMaxTileArea = 4096 * 4096; 90 SkScalar tileArea = SkScalarMul(scaledSize.width(), scaledSize.height()); 91 if (tileArea > kMaxTileArea) { 92 SkScalar clampScale = SkScalarSqrt(SkScalarDiv(kMaxTileArea, tileArea)); 93 scaledSize.set(SkScalarMul(scaledSize.width(), clampScale), 94 SkScalarMul(scaledSize.height(), clampScale)); 95 } 96 97 SkISize tileSize = scaledSize.toRound(); 98 if (tileSize.isEmpty()) { 99 return NULL; 100 } 101 102 // The actual scale, compensating for rounding & clamping. 103 SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(), 104 SkIntToScalar(tileSize.height()) / fTile.height()); 105 106 SkAutoMutexAcquire ama(fCachedBitmapShaderMutex); 107 108 if (!fCachedBitmapShader || tileScale != fCachedTileScale) { 109 SkBitmap bm; 110 if (!bm.tryAllocN32Pixels(tileSize.width(), tileSize.height())) { 111 return NULL; 112 } 113 bm.eraseColor(SK_ColorTRANSPARENT); 114 115 SkCanvas canvas(bm); 116 canvas.scale(tileScale.width(), tileScale.height()); 117 canvas.translate(fTile.x(), fTile.y()); 118 canvas.drawPicture(fPicture); 119 120 fCachedTileScale = tileScale; 121 122 SkMatrix shaderMatrix = this->getLocalMatrix(); 123 shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height()); 124 fCachedBitmapShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix)); 125 } 126 127 // Increment the ref counter inside the mutex to ensure the returned pointer is still valid. 128 // Otherwise, the pointer may have been overwritten on a different thread before the object's 129 // ref count was incremented. 130 fCachedBitmapShader.get()->ref(); 131 return fCachedBitmapShader; 132 } 133 134 size_t SkPictureShader::contextSize() const { 135 return sizeof(PictureShaderContext); 136 } 137 138 SkShader::Context* SkPictureShader::onCreateContext(const ContextRec& rec, void* storage) const { 139 SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix)); 140 if (NULL == bitmapShader.get()) { 141 return NULL; 142 } 143 return PictureShaderContext::Create(storage, *this, rec, bitmapShader); 144 } 145 146 ///////////////////////////////////////////////////////////////////////////////////////// 147 148 SkShader::Context* SkPictureShader::PictureShaderContext::Create(void* storage, 149 const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader) { 150 PictureShaderContext* ctx = SkNEW_PLACEMENT_ARGS(storage, PictureShaderContext, 151 (shader, rec, bitmapShader)); 152 if (NULL == ctx->fBitmapShaderContext) { 153 ctx->~PictureShaderContext(); 154 ctx = NULL; 155 } 156 return ctx; 157 } 158 159 SkPictureShader::PictureShaderContext::PictureShaderContext( 160 const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader) 161 : INHERITED(shader, rec) 162 , fBitmapShader(SkRef(bitmapShader)) 163 { 164 fBitmapShaderContextStorage = sk_malloc_throw(bitmapShader->contextSize()); 165 fBitmapShaderContext = bitmapShader->createContext(rec, fBitmapShaderContextStorage); 166 //if fBitmapShaderContext is null, we are invalid 167 } 168 169 SkPictureShader::PictureShaderContext::~PictureShaderContext() { 170 if (fBitmapShaderContext) { 171 fBitmapShaderContext->~Context(); 172 } 173 sk_free(fBitmapShaderContextStorage); 174 } 175 176 uint32_t SkPictureShader::PictureShaderContext::getFlags() const { 177 SkASSERT(fBitmapShaderContext); 178 return fBitmapShaderContext->getFlags(); 179 } 180 181 SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) { 182 SkASSERT(fBitmapShaderContext); 183 return fBitmapShaderContext->asAShadeProc(ctx); 184 } 185 186 void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { 187 SkASSERT(fBitmapShaderContext); 188 fBitmapShaderContext->shadeSpan(x, y, dstC, count); 189 } 190 191 void SkPictureShader::PictureShaderContext::shadeSpan16(int x, int y, uint16_t dstC[], int count) { 192 SkASSERT(fBitmapShaderContext); 193 fBitmapShaderContext->shadeSpan16(x, y, dstC, count); 194 } 195 196 #ifndef SK_IGNORE_TO_STRING 197 void SkPictureShader::toString(SkString* str) const { 198 static const char* gTileModeName[SkShader::kTileModeCount] = { 199 "clamp", "repeat", "mirror" 200 }; 201 202 str->appendf("PictureShader: [%f:%f:%f:%f] ", 203 fPicture ? fPicture->cullRect().fLeft : 0, 204 fPicture ? fPicture->cullRect().fTop : 0, 205 fPicture ? fPicture->cullRect().fRight : 0, 206 fPicture ? fPicture->cullRect().fBottom : 0); 207 208 str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]); 209 210 this->INHERITED::toString(str); 211 } 212 #endif 213 214 #if SK_SUPPORT_GPU 215 bool SkPictureShader::asFragmentProcessor(GrContext* context, const SkPaint& paint, 216 const SkMatrix* localMatrix, GrColor* paintColor, 217 GrFragmentProcessor** fp) const { 218 SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(context->getMatrix(), localMatrix)); 219 if (!bitmapShader) { 220 return false; 221 } 222 return bitmapShader->asFragmentProcessor(context, paint, NULL, paintColor, fp); 223 } 224 #else 225 bool SkPictureShader::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, GrColor*, 226 GrFragmentProcessor**) const { 227 SkDEBUGFAIL("Should not call in GPU-less build"); 228 return false; 229 } 230 #endif 231