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 76template <typename T> void safe_call_destructor(T* obj) { 77 if (obj) { 78 obj->~T(); 79 } 80} 81 82SkShader::Context* SkComposeShader::onCreateContext(const ContextRec& rec, void* storage) const { 83 char* aStorage = (char*) storage + sizeof(ComposeShaderContext); 84 char* bStorage = aStorage + fShaderA->contextSize(); 85 86 // we preconcat our localMatrix (if any) with the device matrix 87 // before calling our sub-shaders 88 SkMatrix tmpM; 89 tmpM.setConcat(*rec.fMatrix, this->getLocalMatrix()); 90 91 // Our sub-shaders need to see opaque, so by combining them we don't double-alphatize the 92 // result. ComposeShader itself will respect the alpha, and post-apply it after calling the 93 // sub-shaders. 94 SkPaint opaquePaint(*rec.fPaint); 95 opaquePaint.setAlpha(0xFF); 96 97 ContextRec newRec(rec); 98 newRec.fMatrix = &tmpM; 99 newRec.fPaint = &opaquePaint; 100 101 SkShader::Context* contextA = fShaderA->createContext(newRec, aStorage); 102 SkShader::Context* contextB = fShaderB->createContext(newRec, bStorage); 103 if (!contextA || !contextB) { 104 safe_call_destructor(contextA); 105 safe_call_destructor(contextB); 106 return NULL; 107 } 108 109 return SkNEW_PLACEMENT_ARGS(storage, ComposeShaderContext, (*this, rec, contextA, contextB)); 110} 111 112SkComposeShader::ComposeShaderContext::ComposeShaderContext( 113 const SkComposeShader& shader, const ContextRec& rec, 114 SkShader::Context* contextA, SkShader::Context* contextB) 115 : INHERITED(shader, rec) 116 , fShaderContextA(contextA) 117 , fShaderContextB(contextB) {} 118 119SkComposeShader::ComposeShaderContext::~ComposeShaderContext() { 120 fShaderContextA->~Context(); 121 fShaderContextB->~Context(); 122} 123 124bool SkComposeShader::asACompose(ComposeRec* rec) const { 125 if (rec) { 126 rec->fShaderA = fShaderA; 127 rec->fShaderB = fShaderB; 128 rec->fMode = fMode; 129 } 130 return true; 131} 132 133 134// larger is better (fewer times we have to loop), but we shouldn't 135// take up too much stack-space (each element is 4 bytes) 136#define TMP_COLOR_COUNT 64 137 138void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { 139 SkShader::Context* shaderContextA = fShaderContextA; 140 SkShader::Context* shaderContextB = fShaderContextB; 141 SkXfermode* mode = static_cast<const SkComposeShader&>(fShader).fMode; 142 unsigned scale = SkAlpha255To256(this->getPaintAlpha()); 143 144#ifdef SK_BUILD_FOR_ANDROID 145 scale = 256; // ugh -- maintain old bug/behavior for now 146#endif 147 148 SkPMColor tmp[TMP_COLOR_COUNT]; 149 150 if (NULL == mode) { // implied SRC_OVER 151 // TODO: when we have a good test-case, should use SkBlitRow::Proc32 152 // for these loops 153 do { 154 int n = count; 155 if (n > TMP_COLOR_COUNT) { 156 n = TMP_COLOR_COUNT; 157 } 158 159 shaderContextA->shadeSpan(x, y, result, n); 160 shaderContextB->shadeSpan(x, y, tmp, n); 161 162 if (256 == scale) { 163 for (int i = 0; i < n; i++) { 164 result[i] = SkPMSrcOver(tmp[i], result[i]); 165 } 166 } else { 167 for (int i = 0; i < n; i++) { 168 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), 169 scale); 170 } 171 } 172 173 result += n; 174 x += n; 175 count -= n; 176 } while (count > 0); 177 } else { // use mode for the composition 178 do { 179 int n = count; 180 if (n > TMP_COLOR_COUNT) { 181 n = TMP_COLOR_COUNT; 182 } 183 184 shaderContextA->shadeSpan(x, y, result, n); 185 shaderContextB->shadeSpan(x, y, tmp, n); 186 mode->xfer32(result, tmp, n, NULL); 187 188 if (256 != scale) { 189 for (int i = 0; i < n; i++) { 190 result[i] = SkAlphaMulQ(result[i], scale); 191 } 192 } 193 194 result += n; 195 x += n; 196 count -= n; 197 } while (count > 0); 198 } 199} 200 201#ifndef SK_IGNORE_TO_STRING 202void SkComposeShader::toString(SkString* str) const { 203 str->append("SkComposeShader: ("); 204 205 str->append("ShaderA: "); 206 fShaderA->toString(str); 207 str->append(" ShaderB: "); 208 fShaderB->toString(str); 209 str->append(" Xfermode: "); 210 fMode->toString(str); 211 212 this->INHERITED::toString(str); 213 214 str->append(")"); 215} 216#endif 217