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