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