SkComposeShader.cpp revision edda70e020630103270c815b7499e8b02271875d
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 124// larger is better (fewer times we have to loop), but we shouldn't 125// take up too much stack-space (each element is 4 bytes) 126#define TMP_COLOR_COUNT 64 127 128void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { 129 SkShader::Context* shaderContextA = fShaderContextA; 130 SkShader::Context* shaderContextB = fShaderContextB; 131 SkXfermode* mode = static_cast<const SkComposeShader&>(fShader).fMode; 132 unsigned scale = SkAlpha255To256(this->getPaintAlpha()); 133 134#ifdef SK_BUILD_FOR_ANDROID 135 scale = 256; // ugh -- maintain old bug/behavior for now 136#endif 137 138 SkPMColor tmp[TMP_COLOR_COUNT]; 139 140 if (NULL == mode) { // implied SRC_OVER 141 // TODO: when we have a good test-case, should use SkBlitRow::Proc32 142 // for these loops 143 do { 144 int n = count; 145 if (n > TMP_COLOR_COUNT) { 146 n = TMP_COLOR_COUNT; 147 } 148 149 shaderContextA->shadeSpan(x, y, result, n); 150 shaderContextB->shadeSpan(x, y, tmp, n); 151 152 if (256 == scale) { 153 for (int i = 0; i < n; i++) { 154 result[i] = SkPMSrcOver(tmp[i], result[i]); 155 } 156 } else { 157 for (int i = 0; i < n; i++) { 158 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), 159 scale); 160 } 161 } 162 163 result += n; 164 x += n; 165 count -= n; 166 } while (count > 0); 167 } else { // use mode for the composition 168 do { 169 int n = count; 170 if (n > TMP_COLOR_COUNT) { 171 n = TMP_COLOR_COUNT; 172 } 173 174 shaderContextA->shadeSpan(x, y, result, n); 175 shaderContextB->shadeSpan(x, y, tmp, n); 176 mode->xfer32(result, tmp, n, NULL); 177 178 if (256 != scale) { 179 for (int i = 0; i < n; i++) { 180 result[i] = SkAlphaMulQ(result[i], scale); 181 } 182 } 183 184 result += n; 185 x += n; 186 count -= n; 187 } while (count > 0); 188 } 189} 190 191#ifndef SK_IGNORE_TO_STRING 192void SkComposeShader::toString(SkString* str) const { 193 str->append("SkComposeShader: ("); 194 195 str->append("ShaderA: "); 196 fShaderA->toString(str); 197 str->append(" ShaderB: "); 198 fShaderB->toString(str); 199 str->append(" Xfermode: "); 200 fMode->toString(str); 201 202 this->INHERITED::toString(str); 203 204 str->append(")"); 205} 206#endif 207