SkComposeShader.cpp revision e901b6de3ef8dea842008a08fc81e92fb1478d61
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 // TODO : must fix this to not "cheat" and modify fPaint 104 SkAutoAlphaRestore restore(const_cast<SkPaint*>(rec.fPaint), 0xFF); 105 106 char* aStorage = (char*) storage + sizeof(ComposeShaderContext); 107 char* bStorage = aStorage + fShaderA->contextSize(); 108 109 // we preconcat our localMatrix (if any) with the device matrix 110 // before calling our sub-shaders 111 112 SkMatrix tmpM; 113 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix()); 114 115 ContextRec newRec(rec); 116 newRec.fMatrix = &tmpM; 117 118 SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage); 119 SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage); 120 121 // Both functions must succeed; otherwise validContext should have returned 122 // false. 123 SkASSERT(contextA); 124 SkASSERT(contextB); 125 126 return SkNEW_PLACEMENT_ARGS(storage, ComposeShaderContext, (*this, rec, contextA, contextB)); 127} 128 129SkComposeShader::ComposeShaderContext::ComposeShaderContext( 130 const SkComposeShader& shader, const ContextRec& rec, 131 SkShader::Context* contextA, SkShader::Context* contextB) 132 : INHERITED(shader, rec) 133 , fShaderContextA(contextA) 134 , fShaderContextB(contextB) {} 135 136SkComposeShader::ComposeShaderContext::~ComposeShaderContext() { 137 fShaderContextA->~Context(); 138 fShaderContextB->~Context(); 139} 140 141// larger is better (fewer times we have to loop), but we shouldn't 142// take up too much stack-space (each element is 4 bytes) 143#define TMP_COLOR_COUNT 64 144 145void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { 146 SkShader::Context* shaderContextA = fShaderContextA; 147 SkShader::Context* shaderContextB = fShaderContextB; 148 SkXfermode* mode = static_cast<const SkComposeShader&>(fShader).fMode; 149 unsigned scale = SkAlpha255To256(this->getPaintAlpha()); 150 151 SkPMColor tmp[TMP_COLOR_COUNT]; 152 153 if (NULL == mode) { // implied SRC_OVER 154 // TODO: when we have a good test-case, should use SkBlitRow::Proc32 155 // for these loops 156 do { 157 int n = count; 158 if (n > TMP_COLOR_COUNT) { 159 n = TMP_COLOR_COUNT; 160 } 161 162 shaderContextA->shadeSpan(x, y, result, n); 163 shaderContextB->shadeSpan(x, y, tmp, n); 164 165 if (256 == scale) { 166 for (int i = 0; i < n; i++) { 167 result[i] = SkPMSrcOver(tmp[i], result[i]); 168 } 169 } else { 170 for (int i = 0; i < n; i++) { 171 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), 172 scale); 173 } 174 } 175 176 result += n; 177 x += n; 178 count -= n; 179 } while (count > 0); 180 } else { // use mode for the composition 181 do { 182 int n = count; 183 if (n > TMP_COLOR_COUNT) { 184 n = TMP_COLOR_COUNT; 185 } 186 187 shaderContextA->shadeSpan(x, y, result, n); 188 shaderContextB->shadeSpan(x, y, tmp, n); 189 mode->xfer32(result, tmp, n, NULL); 190 191 if (256 == scale) { 192 for (int i = 0; i < n; i++) { 193 result[i] = SkAlphaMulQ(result[i], scale); 194 } 195 } 196 197 result += n; 198 x += n; 199 count -= n; 200 } while (count > 0); 201 } 202} 203 204#ifndef SK_IGNORE_TO_STRING 205void SkComposeShader::toString(SkString* str) const { 206 str->append("SkComposeShader: ("); 207 208 str->append("ShaderA: "); 209 fShaderA->toString(str); 210 str->append(" ShaderB: "); 211 fShaderB->toString(str); 212 str->append(" Xfermode: "); 213 fMode->toString(str); 214 215 this->INHERITED::toString(str); 216 217 str->append(")"); 218} 219#endif 220