SkComposeShader.cpp revision 1ab536f16164795782a75b11efcb17541cbc2e26
1 2/* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkComposeShader.h" 11#include "SkColorFilter.h" 12#include "SkColorPriv.h" 13#include "SkColorShader.h" 14#include "SkReadBuffer.h" 15#include "SkWriteBuffer.h" 16#include "SkXfermode.h" 17#include "SkString.h" 18 19/////////////////////////////////////////////////////////////////////////////// 20 21SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) { 22 fShaderA = sA; sA->ref(); 23 fShaderB = sB; sB->ref(); 24 // mode may be null 25 fMode = mode; 26 SkSafeRef(mode); 27} 28 29SkComposeShader::SkComposeShader(SkReadBuffer& buffer) : 30 INHERITED(buffer) { 31 fShaderA = buffer.readShader(); 32 if (NULL == fShaderA) { 33 fShaderA = SkNEW_ARGS(SkColorShader, ((SkColor)0)); 34 } 35 fShaderB = buffer.readShader(); 36 if (NULL == fShaderB) { 37 fShaderB = SkNEW_ARGS(SkColorShader, ((SkColor)0)); 38 } 39 fMode = buffer.readXfermode(); 40} 41 42SkComposeShader::~SkComposeShader() { 43 SkSafeUnref(fMode); 44 fShaderB->unref(); 45 fShaderA->unref(); 46} 47 48size_t SkComposeShader::contextSize() const { 49 return sizeof(ComposeShaderContext) + fShaderA->contextSize() + fShaderB->contextSize(); 50} 51 52class SkAutoAlphaRestore { 53public: 54 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { 55 fAlpha = paint->getAlpha(); 56 fPaint = paint; 57 paint->setAlpha(newAlpha); 58 } 59 60 ~SkAutoAlphaRestore() { 61 fPaint->setAlpha(fAlpha); 62 } 63private: 64 SkPaint* fPaint; 65 uint8_t fAlpha; 66}; 67#define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore) 68 69void SkComposeShader::flatten(SkWriteBuffer& buffer) const { 70 this->INHERITED::flatten(buffer); 71 buffer.writeFlattenable(fShaderA); 72 buffer.writeFlattenable(fShaderB); 73 buffer.writeFlattenable(fMode); 74} 75 76/* We call validContext/createContext on our two worker shaders. 77 However, we always let them see opaque alpha, and if the paint 78 really is translucent, then we apply that after the fact. 79 80 */ 81bool SkComposeShader::validContext(const ContextRec& rec, SkMatrix* totalInverse) const { 82 if (!this->INHERITED::validContext(rec, totalInverse)) { 83 return false; 84 } 85 86 // we preconcat our localMatrix (if any) with the device matrix 87 // before calling our sub-shaders 88 89 SkMatrix tmpM; 90 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix()); 91 92 ContextRec newRec(rec); 93 newRec.fMatrix = &tmpM; 94 95 return fShaderA->validContext(newRec) && fShaderB->validContext(newRec); 96} 97 98SkShader::Context* SkComposeShader::createContext(const ContextRec& rec, void* storage) const { 99 if (!this->validContext(rec)) { 100 return NULL; 101 } 102 103 char* aStorage = (char*) storage + sizeof(ComposeShaderContext); 104 char* bStorage = aStorage + fShaderA->contextSize(); 105 106 // we preconcat our localMatrix (if any) with the device matrix 107 // before calling our sub-shaders 108 SkMatrix tmpM; 109 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix()); 110 111 // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the 112 // result. ComposeShader itself will respect the alpha, and post-apply it after calling the 113 // sub-shaders. 114 SkPaint opaquePaint(*rec.fPaint); 115 opaquePaint.setAlpha(0xFF); 116 117 ContextRec newRec(rec); 118 newRec.fMatrix = &tmpM; 119 newRec.fPaint = &opaquePaint; 120 121 SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage); 122 SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage); 123 124 // Both functions must succeed; otherwise validContext should have returned 125 // false. 126 SkASSERT(contextA); 127 SkASSERT(contextB); 128 129 return SkNEW_PLACEMENT_ARGS(storage, ComposeShaderContext, (*this, rec, contextA, contextB)); 130} 131 132SkComposeShader::ComposeShaderContext::ComposeShaderContext( 133 const SkComposeShader& shader, const ContextRec& rec, 134 SkShader::Context* contextA, SkShader::Context* contextB) 135 : INHERITED(shader, rec) 136 , fShaderContextA(contextA) 137 , fShaderContextB(contextB) {} 138 139SkComposeShader::ComposeShaderContext::~ComposeShaderContext() { 140 fShaderContextA->~Context(); 141 fShaderContextB->~Context(); 142} 143 144// larger is better (fewer times we have to loop), but we shouldn't 145// take up too much stack-space (each element is 4 bytes) 146#define TMP_COLOR_COUNT 64 147 148void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { 149 SkShader::Context* shaderContextA = fShaderContextA; 150 SkShader::Context* shaderContextB = fShaderContextB; 151 SkXfermode* mode = static_cast<const SkComposeShader&>(fShader).fMode; 152 unsigned scale = SkAlpha255To256(this->getPaintAlpha()); 153 154#ifdef SK_BUILD_FOR_ANDROID 155 scale = 256; // ugh -- maintain old bug/behavior for now 156#endif 157 158 SkPMColor tmp[TMP_COLOR_COUNT]; 159 160 if (NULL == mode) { // implied SRC_OVER 161 // TODO: when we have a good test-case, should use SkBlitRow::Proc32 162 // for these loops 163 do { 164 int n = count; 165 if (n > TMP_COLOR_COUNT) { 166 n = TMP_COLOR_COUNT; 167 } 168 169 shaderContextA->shadeSpan(x, y, result, n); 170 shaderContextB->shadeSpan(x, y, tmp, n); 171 172 if (256 == scale) { 173 for (int i = 0; i < n; i++) { 174 result[i] = SkPMSrcOver(tmp[i], result[i]); 175 } 176 } else { 177 for (int i = 0; i < n; i++) { 178 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), 179 scale); 180 } 181 } 182 183 result += n; 184 x += n; 185 count -= n; 186 } while (count > 0); 187 } else { // use mode for the composition 188 do { 189 int n = count; 190 if (n > TMP_COLOR_COUNT) { 191 n = TMP_COLOR_COUNT; 192 } 193 194 shaderContextA->shadeSpan(x, y, result, n); 195 shaderContextB->shadeSpan(x, y, tmp, n); 196 mode->xfer32(result, tmp, n, NULL); 197 198 if (256 != scale) { 199 for (int i = 0; i < n; i++) { 200 result[i] = SkAlphaMulQ(result[i], scale); 201 } 202 } 203 204 result += n; 205 x += n; 206 count -= n; 207 } while (count > 0); 208 } 209} 210 211#ifndef SK_IGNORE_TO_STRING 212void SkComposeShader::toString(SkString* str) const { 213 str->append("SkComposeShader: ("); 214 215 str->append("ShaderA: "); 216 fShaderA->toString(str); 217 str->append(" ShaderB: "); 218 fShaderB->toString(str); 219 str->append(" Xfermode: "); 220 fMode->toString(str); 221 222 this->INHERITED::toString(str); 223 224 str->append(")"); 225} 226#endif 227