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 "SkComposeShader.h" 10#include "SkColorFilter.h" 11#include "SkColorPriv.h" 12#include "SkColorShader.h" 13#include "SkReadBuffer.h" 14#include "SkWriteBuffer.h" 15#include "SkString.h" 16 17sk_sp<SkShader> SkShader::MakeComposeShader(sk_sp<SkShader> dst, sk_sp<SkShader> src, 18 SkBlendMode mode) { 19 if (!src || !dst) { 20 return nullptr; 21 } 22 if (SkBlendMode::kSrc == mode) { 23 return src; 24 } 25 if (SkBlendMode::kDst == mode) { 26 return dst; 27 } 28 return sk_sp<SkShader>(new SkComposeShader(std::move(dst), std::move(src), mode)); 29} 30 31/////////////////////////////////////////////////////////////////////////////// 32 33class SkAutoAlphaRestore { 34public: 35 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { 36 fAlpha = paint->getAlpha(); 37 fPaint = paint; 38 paint->setAlpha(newAlpha); 39 } 40 41 ~SkAutoAlphaRestore() { 42 fPaint->setAlpha(fAlpha); 43 } 44private: 45 SkPaint* fPaint; 46 uint8_t fAlpha; 47}; 48#define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore) 49 50sk_sp<SkFlattenable> SkComposeShader::CreateProc(SkReadBuffer& buffer) { 51 sk_sp<SkShader> shaderA(buffer.readShader()); 52 sk_sp<SkShader> shaderB(buffer.readShader()); 53 SkBlendMode mode; 54 if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode2_Version)) { 55 sk_sp<SkXfermode> xfer = buffer.readXfermode(); 56 mode = xfer ? xfer->blend() : SkBlendMode::kSrcOver; 57 } else { 58 mode = (SkBlendMode)buffer.read32(); 59 } 60 if (!shaderA || !shaderB) { 61 return nullptr; 62 } 63 return sk_make_sp<SkComposeShader>(std::move(shaderA), std::move(shaderB), mode); 64} 65 66void SkComposeShader::flatten(SkWriteBuffer& buffer) const { 67 buffer.writeFlattenable(fShaderA.get()); 68 buffer.writeFlattenable(fShaderB.get()); 69 buffer.write32((int)fMode); 70} 71 72SkShader::Context* SkComposeShader::onMakeContext( 73 const ContextRec& rec, SkArenaAlloc* alloc) const 74{ 75 // we preconcat our localMatrix (if any) with the device matrix 76 // before calling our sub-shaders 77 SkMatrix tmpM; 78 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix()); 79 80 // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the 81 // result. ComposeShader itself will respect the alpha, and post-apply it after calling the 82 // sub-shaders. 83 SkPaint opaquePaint(*rec.fPaint); 84 opaquePaint.setAlpha(0xFF); 85 86 ContextRec newRec(rec); 87 newRec.fMatrix = &tmpM; 88 newRec.fPaint = &opaquePaint; 89 90 SkShader::Context* contextA = fShaderA->makeContext(newRec, alloc); 91 SkShader::Context* contextB = fShaderB->makeContext(newRec, alloc); 92 if (!contextA || !contextB) { 93 return nullptr; 94 } 95 96 return alloc->make<ComposeShaderContext>(*this, rec, contextA, contextB); 97} 98 99SkComposeShader::ComposeShaderContext::ComposeShaderContext( 100 const SkComposeShader& shader, const ContextRec& rec, 101 SkShader::Context* contextA, SkShader::Context* contextB) 102 : INHERITED(shader, rec) 103 , fShaderContextA(contextA) 104 , fShaderContextB(contextB) {} 105 106bool SkComposeShader::asACompose(ComposeRec* rec) const { 107 if (rec) { 108 rec->fShaderA = fShaderA.get(); 109 rec->fShaderB = fShaderB.get(); 110 rec->fBlendMode = fMode; 111 } 112 return true; 113} 114 115 116// larger is better (fewer times we have to loop), but we shouldn't 117// take up too much stack-space (each element is 4 bytes) 118#define TMP_COLOR_COUNT 64 119 120void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { 121 SkShader::Context* shaderContextA = fShaderContextA; 122 SkShader::Context* shaderContextB = fShaderContextB; 123 SkBlendMode mode = static_cast<const SkComposeShader&>(fShader).fMode; 124 unsigned scale = SkAlpha255To256(this->getPaintAlpha()); 125 126 SkPMColor tmp[TMP_COLOR_COUNT]; 127 128 SkXfermode* xfer = SkXfermode::Peek(mode); 129 if (nullptr == xfer) { // implied SRC_OVER 130 // TODO: when we have a good test-case, should use SkBlitRow::Proc32 131 // for these loops 132 do { 133 int n = count; 134 if (n > TMP_COLOR_COUNT) { 135 n = TMP_COLOR_COUNT; 136 } 137 138 shaderContextA->shadeSpan(x, y, result, n); 139 shaderContextB->shadeSpan(x, y, tmp, n); 140 141 if (256 == scale) { 142 for (int i = 0; i < n; i++) { 143 result[i] = SkPMSrcOver(tmp[i], result[i]); 144 } 145 } else { 146 for (int i = 0; i < n; i++) { 147 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), 148 scale); 149 } 150 } 151 152 result += n; 153 x += n; 154 count -= n; 155 } while (count > 0); 156 } else { // use mode for the composition 157 do { 158 int n = count; 159 if (n > TMP_COLOR_COUNT) { 160 n = TMP_COLOR_COUNT; 161 } 162 163 shaderContextA->shadeSpan(x, y, result, n); 164 shaderContextB->shadeSpan(x, y, tmp, n); 165 xfer->xfer32(result, tmp, n, nullptr); 166 167 if (256 != scale) { 168 for (int i = 0; i < n; i++) { 169 result[i] = SkAlphaMulQ(result[i], scale); 170 } 171 } 172 173 result += n; 174 x += n; 175 count -= n; 176 } while (count > 0); 177 } 178} 179 180#if SK_SUPPORT_GPU 181 182#include "effects/GrConstColorProcessor.h" 183#include "effects/GrXfermodeFragmentProcessor.h" 184 185///////////////////////////////////////////////////////////////////// 186 187sk_sp<GrFragmentProcessor> SkComposeShader::asFragmentProcessor(const AsFPArgs& args) const { 188 switch (fMode) { 189 case SkBlendMode::kClear: 190 return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), 191 GrConstColorProcessor::kIgnore_InputMode); 192 break; 193 case SkBlendMode::kSrc: 194 return fShaderB->asFragmentProcessor(args); 195 break; 196 case SkBlendMode::kDst: 197 return fShaderA->asFragmentProcessor(args); 198 break; 199 default: 200 sk_sp<GrFragmentProcessor> fpA(fShaderA->asFragmentProcessor(args)); 201 if (!fpA) { 202 return nullptr; 203 } 204 sk_sp<GrFragmentProcessor> fpB(fShaderB->asFragmentProcessor(args)); 205 if (!fpB) { 206 return nullptr; 207 } 208 return GrXfermodeFragmentProcessor::MakeFromTwoProcessors(std::move(fpB), 209 std::move(fpA), fMode); 210 } 211} 212#endif 213 214#ifndef SK_IGNORE_TO_STRING 215void SkComposeShader::toString(SkString* str) const { 216 str->append("SkComposeShader: ("); 217 218 str->append("ShaderA: "); 219 fShaderA->toString(str); 220 str->append(" ShaderB: "); 221 fShaderB->toString(str); 222 if (SkBlendMode::kSrcOver != fMode) { 223 str->appendf(" Xfermode: %s", SkXfermode::ModeName(fMode)); 224 } 225 226 this->INHERITED::toString(str); 227 228 str->append(")"); 229} 230#endif 231