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 "SkFlattenableBuffers.h" 15 #include "SkXfermode.h" 16 #include "SkString.h" 17 18 /////////////////////////////////////////////////////////////////////////////// 19 20 SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) { 21 fShaderA = sA; sA->ref(); 22 fShaderB = sB; sB->ref(); 23 // mode may be null 24 fMode = mode; 25 SkSafeRef(mode); 26 } 27 28 SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) : 29 INHERITED(buffer) { 30 fShaderA = buffer.readFlattenableT<SkShader>(); 31 if (NULL == fShaderA) { 32 fShaderA = SkNEW_ARGS(SkColorShader, (0)); 33 } 34 fShaderB = buffer.readFlattenableT<SkShader>(); 35 if (NULL == fShaderB) { 36 fShaderB = SkNEW_ARGS(SkColorShader, (0)); 37 } 38 fMode = buffer.readFlattenableT<SkXfermode>(); 39 } 40 41 SkComposeShader::~SkComposeShader() { 42 SkSafeUnref(fMode); 43 fShaderB->unref(); 44 fShaderA->unref(); 45 } 46 47 class SkAutoAlphaRestore { 48 public: 49 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) { 50 fAlpha = paint->getAlpha(); 51 fPaint = paint; 52 paint->setAlpha(newAlpha); 53 } 54 55 ~SkAutoAlphaRestore() { 56 fPaint->setAlpha(fAlpha); 57 } 58 private: 59 SkPaint* fPaint; 60 uint8_t fAlpha; 61 }; 62 63 void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) const { 64 this->INHERITED::flatten(buffer); 65 buffer.writeFlattenable(fShaderA); 66 buffer.writeFlattenable(fShaderB); 67 buffer.writeFlattenable(fMode); 68 } 69 70 /* We call setContext on our two worker shaders. However, we 71 always let them see opaque alpha, and if the paint really 72 is translucent, then we apply that after the fact. 73 74 We need to keep the calls to setContext/endContext balanced, since if we 75 return false, our endContext() will not be called. 76 */ 77 bool SkComposeShader::setContext(const SkBitmap& device, 78 const SkPaint& paint, 79 const SkMatrix& matrix) { 80 if (!this->INHERITED::setContext(device, paint, matrix)) { 81 return false; 82 } 83 84 // we preconcat our localMatrix (if any) with the device matrix 85 // before calling our sub-shaders 86 87 SkMatrix tmpM; 88 89 tmpM.setConcat(matrix, this->getLocalMatrix()); 90 91 SkAutoAlphaRestore restore(const_cast<SkPaint*>(&paint), 0xFF); 92 93 bool setContextA = fShaderA->setContext(device, paint, tmpM); 94 bool setContextB = fShaderB->setContext(device, paint, tmpM); 95 if (!setContextA || !setContextB) { 96 if (setContextB) { 97 fShaderB->endContext(); 98 } 99 else if (setContextA) { 100 fShaderA->endContext(); 101 } 102 this->INHERITED::endContext(); 103 return false; 104 } 105 return true; 106 } 107 108 void SkComposeShader::endContext() { 109 fShaderB->endContext(); 110 fShaderA->endContext(); 111 this->INHERITED::endContext(); 112 } 113 114 // larger is better (fewer times we have to loop), but we shouldn't 115 // take up too much stack-space (each element is 4 bytes) 116 #define TMP_COLOR_COUNT 64 117 118 void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) { 119 SkShader* shaderA = fShaderA; 120 SkShader* shaderB = fShaderB; 121 SkXfermode* mode = fMode; 122 unsigned scale = SkAlpha255To256(this->getPaintAlpha()); 123 124 SkPMColor tmp[TMP_COLOR_COUNT]; 125 126 if (NULL == mode) { // implied SRC_OVER 127 // TODO: when we have a good test-case, should use SkBlitRow::Proc32 128 // for these loops 129 do { 130 int n = count; 131 if (n > TMP_COLOR_COUNT) { 132 n = TMP_COLOR_COUNT; 133 } 134 135 shaderA->shadeSpan(x, y, result, n); 136 shaderB->shadeSpan(x, y, tmp, n); 137 138 if (256 == scale) { 139 for (int i = 0; i < n; i++) { 140 result[i] = SkPMSrcOver(tmp[i], result[i]); 141 } 142 } else { 143 for (int i = 0; i < n; i++) { 144 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), 145 scale); 146 } 147 } 148 149 result += n; 150 x += n; 151 count -= n; 152 } while (count > 0); 153 } else { // use mode for the composition 154 do { 155 int n = count; 156 if (n > TMP_COLOR_COUNT) { 157 n = TMP_COLOR_COUNT; 158 } 159 160 shaderA->shadeSpan(x, y, result, n); 161 shaderB->shadeSpan(x, y, tmp, n); 162 mode->xfer32(result, tmp, n, NULL); 163 164 if (256 == scale) { 165 for (int i = 0; i < n; i++) { 166 result[i] = SkAlphaMulQ(result[i], scale); 167 } 168 } 169 170 result += n; 171 x += n; 172 count -= n; 173 } while (count > 0); 174 } 175 } 176 177 #ifdef SK_DEVELOPER 178 void SkComposeShader::toString(SkString* str) const { 179 str->append("SkComposeShader: ("); 180 181 str->append("ShaderA: "); 182 fShaderA->toString(str); 183 str->append(" ShaderB: "); 184 fShaderB->toString(str); 185 str->append(" Xfermode: "); 186 // TODO: add "fMode->toString(str);" once SkXfermode::toString is added 187 188 this->INHERITED::toString(str); 189 190 str->append(")"); 191 } 192 #endif 193