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