1 /* 2 * Copyright 2006 The Android Open Source Project 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 "SkArenaAlloc.h" 9 #include "SkAtomics.h" 10 #include "SkBitmapProcShader.h" 11 #include "SkColorShader.h" 12 #include "SkColorSpaceXformer.h" 13 #include "SkEmptyShader.h" 14 #include "SkMallocPixelRef.h" 15 #include "SkPaint.h" 16 #include "SkPicture.h" 17 #include "SkPictureShader.h" 18 #include "SkPM4fPriv.h" 19 #include "SkRasterPipeline.h" 20 #include "SkReadBuffer.h" 21 #include "SkScalar.h" 22 #include "SkShaderBase.h" 23 #include "SkTLazy.h" 24 #include "SkWriteBuffer.h" 25 #include "../jumper/SkJumper.h" 26 27 #if SK_SUPPORT_GPU 28 #include "GrFragmentProcessor.h" 29 #endif 30 31 //#define SK_TRACK_SHADER_LIFETIME 32 33 #ifdef SK_TRACK_SHADER_LIFETIME 34 static int32_t gShaderCounter; 35 #endif 36 37 static inline void inc_shader_counter() { 38 #ifdef SK_TRACK_SHADER_LIFETIME 39 int32_t prev = sk_atomic_inc(&gShaderCounter); 40 SkDebugf("+++ shader counter %d\n", prev + 1); 41 #endif 42 } 43 static inline void dec_shader_counter() { 44 #ifdef SK_TRACK_SHADER_LIFETIME 45 int32_t prev = sk_atomic_dec(&gShaderCounter); 46 SkDebugf("--- shader counter %d\n", prev - 1); 47 #endif 48 } 49 50 SkShaderBase::SkShaderBase(const SkMatrix* localMatrix) 51 : fLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I()) { 52 inc_shader_counter(); 53 // Pre-cache so future calls to fLocalMatrix.getType() are threadsafe. 54 (void)fLocalMatrix.getType(); 55 } 56 57 SkShaderBase::~SkShaderBase() { 58 dec_shader_counter(); 59 } 60 61 void SkShaderBase::flatten(SkWriteBuffer& buffer) const { 62 this->INHERITED::flatten(buffer); 63 bool hasLocalM = !fLocalMatrix.isIdentity(); 64 buffer.writeBool(hasLocalM); 65 if (hasLocalM) { 66 buffer.writeMatrix(fLocalMatrix); 67 } 68 } 69 70 bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm, 71 const SkMatrix* outerLocalMatrix, 72 SkMatrix* totalInverse) const { 73 SkMatrix total = SkMatrix::Concat(ctm, fLocalMatrix); 74 if (outerLocalMatrix) { 75 total.preConcat(*outerLocalMatrix); 76 } 77 78 return total.invert(totalInverse); 79 } 80 81 bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const { 82 SkColor storage; 83 if (nullptr == colorPtr) { 84 colorPtr = &storage; 85 } 86 if (this->onAsLuminanceColor(colorPtr)) { 87 *colorPtr = SkColorSetA(*colorPtr, 0xFF); // we only return opaque 88 return true; 89 } 90 return false; 91 } 92 93 SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const { 94 return this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr) 95 ? this->onMakeContext(rec, alloc) 96 : nullptr; 97 } 98 99 SkShaderBase::Context* SkShaderBase::makeBurstPipelineContext(const ContextRec& rec, 100 SkArenaAlloc* alloc) const { 101 102 SkASSERT(rec.fPreferredDstType == ContextRec::kPM4f_DstType); 103 104 // Always use vanilla stages for perspective. 105 if (rec.fMatrix->hasPerspective() || fLocalMatrix.hasPerspective()) { 106 return nullptr; 107 } 108 109 return this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr) 110 ? this->onMakeBurstPipelineContext(rec, alloc) 111 : nullptr; 112 } 113 114 SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec) 115 : fShader(shader), fCTM(*rec.fMatrix) 116 { 117 // We should never use a context for RP-only shaders. 118 SkASSERT(!shader.isRasterPipelineOnly()); 119 // ... or for perspective. 120 SkASSERT(!rec.fMatrix->hasPerspective()); 121 SkASSERT(!rec.fLocalMatrix || !rec.fLocalMatrix->hasPerspective()); 122 123 // Because the context parameters must be valid at this point, we know that the matrix is 124 // invertible. 125 SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse)); 126 127 fPaintAlpha = rec.fPaint->getAlpha(); 128 } 129 130 SkShaderBase::Context::~Context() {} 131 132 SkShaderBase::Context::ShadeProc SkShaderBase::Context::asAShadeProc(void** ctx) { 133 return nullptr; 134 } 135 136 void SkShaderBase::Context::shadeSpan4f(int x, int y, SkPM4f dst[], int count) { 137 const int N = 128; 138 SkPMColor tmp[N]; 139 while (count > 0) { 140 int n = SkTMin(count, N); 141 this->shadeSpan(x, y, tmp, n); 142 for (int i = 0; i < n; ++i) { 143 dst[i] = SkPM4f::FromPMColor(tmp[i]); 144 } 145 dst += n; 146 x += n; 147 count -= n; 148 } 149 } 150 151 #include "SkColorPriv.h" 152 153 #define kTempColorQuadCount 6 // balance between speed (larger) and saving stack-space 154 #define kTempColorCount (kTempColorQuadCount << 2) 155 156 #ifdef SK_CPU_BENDIAN 157 #define SkU32BitShiftToByteOffset(shift) (3 - ((shift) >> 3)) 158 #else 159 #define SkU32BitShiftToByteOffset(shift) ((shift) >> 3) 160 #endif 161 162 void SkShaderBase::Context::shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) { 163 SkASSERT(count > 0); 164 165 SkPMColor colors[kTempColorCount]; 166 167 while ((count -= kTempColorCount) >= 0) { 168 this->shadeSpan(x, y, colors, kTempColorCount); 169 x += kTempColorCount; 170 171 const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); 172 int quads = kTempColorQuadCount; 173 do { 174 U8CPU a0 = srcA[0]; 175 U8CPU a1 = srcA[4]; 176 U8CPU a2 = srcA[8]; 177 U8CPU a3 = srcA[12]; 178 srcA += 4*4; 179 *alpha++ = SkToU8(a0); 180 *alpha++ = SkToU8(a1); 181 *alpha++ = SkToU8(a2); 182 *alpha++ = SkToU8(a3); 183 } while (--quads != 0); 184 } 185 SkASSERT(count < 0); 186 SkASSERT(count + kTempColorCount >= 0); 187 if (count += kTempColorCount) { 188 this->shadeSpan(x, y, colors, count); 189 190 const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); 191 do { 192 *alpha++ = *srcA; 193 srcA += 4; 194 } while (--count != 0); 195 } 196 #if 0 197 do { 198 int n = count; 199 if (n > kTempColorCount) 200 n = kTempColorCount; 201 SkASSERT(n > 0); 202 203 this->shadeSpan(x, y, colors, n); 204 x += n; 205 count -= n; 206 207 const uint8_t* srcA = (const uint8_t*)colors + SkU32BitShiftToByteOffset(SK_A32_SHIFT); 208 do { 209 *alpha++ = *srcA; 210 srcA += 4; 211 } while (--n != 0); 212 } while (count > 0); 213 #endif 214 } 215 216 ////////////////////////////////////////////////////////////////////////////// 217 218 const SkMatrix& SkShader::getLocalMatrix() const { 219 return as_SB(this)->getLocalMatrix(); 220 } 221 222 #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP 223 bool SkShader::isABitmap(SkBitmap* outTexture, SkMatrix* outMatrix, TileMode xy[2]) const { 224 return as_SB(this)->onIsABitmap(outTexture, outMatrix, xy); 225 } 226 #endif 227 228 SkImage* SkShader::isAImage(SkMatrix* localMatrix, TileMode xy[2]) const { 229 return as_SB(this)->onIsAImage(localMatrix, xy); 230 } 231 232 SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const { 233 return kNone_GradientType; 234 } 235 236 #if SK_SUPPORT_GPU 237 sk_sp<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const AsFPArgs&) const { 238 return nullptr; 239 } 240 #endif 241 242 sk_sp<SkShader> SkShader::makeAsALocalMatrixShader(SkMatrix*) const { 243 return nullptr; 244 } 245 246 sk_sp<SkShader> SkShader::MakeEmptyShader() { return sk_make_sp<SkEmptyShader>(); } 247 248 sk_sp<SkShader> SkShader::MakeColorShader(SkColor color) { return sk_make_sp<SkColorShader>(color); } 249 250 sk_sp<SkShader> SkShader::MakeBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy, 251 const SkMatrix* localMatrix) { 252 if (localMatrix && !localMatrix->invert(nullptr)) { 253 return nullptr; 254 } 255 return SkMakeBitmapShader(src, tmx, tmy, localMatrix, kIfMutable_SkCopyPixelsMode); 256 } 257 258 sk_sp<SkShader> SkShader::MakePictureShader(sk_sp<SkPicture> src, TileMode tmx, TileMode tmy, 259 const SkMatrix* localMatrix, const SkRect* tile) { 260 if (localMatrix && !localMatrix->invert(nullptr)) { 261 return nullptr; 262 } 263 return SkPictureShader::Make(std::move(src), tmx, tmy, localMatrix, tile); 264 } 265 266 #ifndef SK_IGNORE_TO_STRING 267 void SkShaderBase::toString(SkString* str) const { 268 if (!fLocalMatrix.isIdentity()) { 269 str->append(" "); 270 fLocalMatrix.toString(str); 271 } 272 } 273 #endif 274 275 bool SkShaderBase::appendStages(SkRasterPipeline* p, 276 SkColorSpace* dstCS, 277 SkArenaAlloc* alloc, 278 const SkMatrix& ctm, 279 const SkPaint& paint, 280 const SkMatrix* localM) const { 281 return this->onAppendStages(p, dstCS, alloc, ctm, paint, localM); 282 } 283 284 bool SkShaderBase::onAppendStages(SkRasterPipeline* p, 285 SkColorSpace* dstCS, 286 SkArenaAlloc* alloc, 287 const SkMatrix& ctm, 288 const SkPaint& paint, 289 const SkMatrix* localM) const { 290 // SkShader::Context::shadeSpan4f() handles the paint opacity internally, 291 // but SkRasterPipelineBlitter applies it as a separate stage. 292 // We skip the internal shadeSpan4f() step by forcing the paint opaque. 293 SkTCopyOnFirstWrite<SkPaint> opaquePaint(paint); 294 if (paint.getAlpha() != SK_AlphaOPAQUE) { 295 opaquePaint.writable()->setAlpha(SK_AlphaOPAQUE); 296 } 297 298 ContextRec rec(*opaquePaint, ctm, localM, ContextRec::kPM4f_DstType, dstCS); 299 300 struct CallbackCtx : SkJumper_CallbackCtx { 301 sk_sp<SkShader> shader; 302 Context* ctx; 303 }; 304 auto cb = alloc->make<CallbackCtx>(); 305 cb->shader = dstCS ? SkColorSpaceXformer::Make(sk_ref_sp(dstCS))->apply(this) 306 : sk_ref_sp((SkShader*)this); 307 cb->ctx = as_SB(cb->shader)->makeContext(rec, alloc); 308 cb->fn = [](SkJumper_CallbackCtx* self, int active_pixels) { 309 auto c = (CallbackCtx*)self; 310 int x = (int)c->rgba[0], 311 y = (int)c->rgba[1]; 312 c->ctx->shadeSpan4f(x,y, (SkPM4f*)c->rgba, active_pixels); 313 }; 314 315 if (cb->ctx) { 316 p->append(SkRasterPipeline::seed_shader); 317 p->append(SkRasterPipeline::callback, cb); 318 return true; 319 } 320 return false; 321 } 322 323 /////////////////////////////////////////////////////////////////////////////////////////////////// 324 325 sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) { 326 return SkShader::MakeEmptyShader(); 327 } 328 329 #ifndef SK_IGNORE_TO_STRING 330 #include "SkEmptyShader.h" 331 332 void SkEmptyShader::toString(SkString* str) const { 333 str->append("SkEmptyShader: ("); 334 335 this->INHERITED::toString(str); 336 337 str->append(")"); 338 } 339 #endif 340