SkComposeShader.cpp revision c4cae85752e3e486cf4eac8cd8128f57b6f40563
1/* libs/graphics/effects/SkShaderExtras.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkComposeShader.h"
19#include "SkColorFilter.h"
20#include "SkColorPriv.h"
21#include "SkXfermode.h"
22
23//////////////////////////////////////////////////////////////////////////////////////
24
25SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode)
26{
27    fShaderA = sA;  sA->ref();
28    fShaderB = sB;  sB->ref();
29    // mode may be null
30    fMode = mode;   mode->safeRef();
31}
32
33SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
34    INHERITED(buffer)
35{
36    fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
37    fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
38    fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
39}
40
41SkComposeShader::~SkComposeShader()
42{
43    fMode->safeUnref(); // may be null
44    fShaderB->unref();
45    fShaderA->unref();
46}
47
48void SkComposeShader::beginSession()
49{
50    this->INHERITED::beginSession();
51    fShaderA->beginSession();
52    fShaderB->beginSession();
53}
54
55void SkComposeShader::endSession()
56{
57    fShaderA->endSession();
58    fShaderB->endSession();
59    this->INHERITED::endSession();
60}
61
62class SkAutoAlphaRestore {
63public:
64    SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha)
65    {
66        fAlpha = paint->getAlpha();
67        fPaint = paint;
68        paint->setAlpha(newAlpha);
69    }
70    ~SkAutoAlphaRestore()
71    {
72        fPaint->setAlpha(fAlpha);
73    }
74private:
75    SkPaint*    fPaint;
76    uint8_t     fAlpha;
77};
78
79void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer)
80{
81    this->INHERITED::flatten(buffer);
82    buffer.writeFlattenable(fShaderA);
83    buffer.writeFlattenable(fShaderB);
84    buffer.writeFlattenable(fMode);
85}
86
87/*  We call setContext on our two worker shaders. However, we
88    always let them see opaque alpha, and if the paint really
89    is translucent, then we apply that after the fact.
90*/
91bool SkComposeShader::setContext(const SkBitmap& device,
92                                 const SkPaint& paint,
93                                 const SkMatrix& matrix)
94{
95    if (!this->INHERITED::setContext(device, paint, matrix))
96        return false;
97
98    // we preconcat our localMatrix (if any) with the device matrix
99    // before calling our sub-shaders
100
101    SkMatrix tmpM;
102
103    (void)this->getLocalMatrix(&tmpM);
104    tmpM.setConcat(matrix, tmpM);
105
106    SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
107
108    return  fShaderA->setContext(device, paint, tmpM) &&
109            fShaderB->setContext(device, paint, tmpM);
110}
111
112// larger is better (fewer times we have to loop), but we shouldn't
113// take up too much stack-space (each element is 4 bytes)
114#define TMP_COLOR_COUNT     64
115
116void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count)
117{
118    SkShader*   shaderA = fShaderA;
119    SkShader*   shaderB = fShaderB;
120    SkXfermode* mode = fMode;
121    unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
122
123    SkPMColor   tmp[TMP_COLOR_COUNT];
124
125    if (NULL == mode)   // implied SRC_OVER
126    {
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            shaderA->shadeSpan(x, y, result, n);
135            shaderB->shadeSpan(x, y, tmp, n);
136
137            if (256 == scale)
138            {
139                for (int i = 0; i < n; i++)
140                    result[i] = SkPMSrcOver(tmp[i], result[i]);
141            }
142            else
143            {
144                for (int i = 0; i < n; i++)
145                    result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]), scale);
146            }
147
148            result += n;
149            x += n;
150            count -= n;
151        } while (count > 0);
152    }
153    else    // use mode for the composition
154    {
155        do {
156            int n = count;
157            if (n > TMP_COLOR_COUNT)
158                n = TMP_COLOR_COUNT;
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            {
166                for (int i = 0; i < n; i++)
167                    result[i] = SkAlphaMulQ(result[i], scale);
168            }
169
170            result += n;
171            x += n;
172            count -= n;
173        } while (count > 0);
174    }
175}
176
177