3 * Copyright 2006 The Android Open Source Project
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
10 #include "SkComposeShader.h"
11 #include "SkColorFilter.h"
12 #include "SkColorPriv.h"
13 #include "SkColorShader.h"
14 #include "SkFlattenableBuffers.h"
15 #include "SkXfermode.h"
18 ///////////////////////////////////////////////////////////////////////////////
20 SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
21 fShaderA = sA; sA->ref();
22 fShaderB = sB; sB->ref();
28 SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
30 fShaderA = buffer.readShader();
31 if (NULL == fShaderA) {
32 fShaderA = SkNEW_ARGS(SkColorShader, (0));
34 fShaderB = buffer.readShader();
35 if (NULL == fShaderB) {
36 fShaderB = SkNEW_ARGS(SkColorShader, (0));
38 fMode = buffer.readXfermode();
41 SkComposeShader::~SkComposeShader() {
47 class SkAutoAlphaRestore {
49 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
50 fAlpha = paint->getAlpha();
52 paint->setAlpha(newAlpha);
55 ~SkAutoAlphaRestore() {
56 fPaint->setAlpha(fAlpha);
62 #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore)
64 void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) const {
65 this->INHERITED::flatten(buffer);
66 buffer.writeFlattenable(fShaderA);
67 buffer.writeFlattenable(fShaderB);
68 buffer.writeFlattenable(fMode);
71 /* We call setContext on our two worker shaders. However, we
72 always let them see opaque alpha, and if the paint really
73 is translucent, then we apply that after the fact.
75 We need to keep the calls to setContext/endContext balanced, since if we
76 return false, our endContext() will not be called.
78 bool SkComposeShader::setContext(const SkBitmap& device,
80 const SkMatrix& matrix) {
81 if (!this->INHERITED::setContext(device, paint, matrix)) {
85 // we preconcat our localMatrix (if any) with the device matrix
86 // before calling our sub-shaders
90 tmpM.setConcat(matrix, this->getLocalMatrix());
92 SkAutoAlphaRestore restore(const_cast<SkPaint*>(&paint), 0xFF);
94 bool setContextA = fShaderA->setContext(device, paint, tmpM);
95 bool setContextB = fShaderB->setContext(device, paint, tmpM);
96 if (!setContextA || !setContextB) {
98 fShaderB->endContext();
100 else if (setContextA) {
101 fShaderA->endContext();
103 this->INHERITED::endContext();
109 void SkComposeShader::endContext() {
110 fShaderB->endContext();
111 fShaderA->endContext();
112 this->INHERITED::endContext();
115 // larger is better (fewer times we have to loop), but we shouldn't
116 // take up too much stack-space (each element is 4 bytes)
117 #define TMP_COLOR_COUNT 64
119 void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
120 SkShader* shaderA = fShaderA;
121 SkShader* shaderB = fShaderB;
122 SkXfermode* mode = fMode;
123 unsigned scale = SkAlpha255To256(this->getPaintAlpha());
125 SkPMColor tmp[TMP_COLOR_COUNT];
127 if (NULL == mode) { // implied SRC_OVER
128 // TODO: when we have a good test-case, should use SkBlitRow::Proc32
132 if (n > TMP_COLOR_COUNT) {
136 shaderA->shadeSpan(x, y, result, n);
137 shaderB->shadeSpan(x, y, tmp, n);
140 for (int i = 0; i < n; i++) {
141 result[i] = SkPMSrcOver(tmp[i], result[i]);
144 for (int i = 0; i < n; i++) {
145 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
154 } else { // use mode for the composition
157 if (n > TMP_COLOR_COUNT) {
161 shaderA->shadeSpan(x, y, result, n);
162 shaderB->shadeSpan(x, y, tmp, n);
163 mode->xfer32(result, tmp, n, NULL);
166 for (int i = 0; i < n; i++) {
167 result[i] = SkAlphaMulQ(result[i], scale);
179 void SkComposeShader::toString(SkString* str) const {
180 str->append("SkComposeShader: (");
182 str->append("ShaderA: ");
183 fShaderA->toString(str);
184 str->append(" ShaderB: ");
185 fShaderB->toString(str);
186 str->append(" Xfermode: ");
187 fMode->toString(str);
189 this->INHERITED::toString(str);