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
SkComposeShader(SkShader * sA,SkShader * sB,SkXfermode * mode)25 SkComposeShader::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
SkComposeShader(SkFlattenableReadBuffer & buffer)33 SkComposeShader::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
~SkComposeShader()41 SkComposeShader::~SkComposeShader()
42 {
43 fMode->safeUnref(); // may be null
44 fShaderB->unref();
45 fShaderA->unref();
46 }
47
beginSession()48 void SkComposeShader::beginSession()
49 {
50 this->INHERITED::beginSession();
51 fShaderA->beginSession();
52 fShaderB->beginSession();
53 }
54
endSession()55 void SkComposeShader::endSession()
56 {
57 fShaderA->endSession();
58 fShaderB->endSession();
59 this->INHERITED::endSession();
60 }
61
62 class SkAutoAlphaRestore {
63 public:
SkAutoAlphaRestore(SkPaint * paint,uint8_t newAlpha)64 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha)
65 {
66 fAlpha = paint->getAlpha();
67 fPaint = paint;
68 paint->setAlpha(newAlpha);
69 }
~SkAutoAlphaRestore()70 ~SkAutoAlphaRestore()
71 {
72 fPaint->setAlpha(fAlpha);
73 }
74 private:
75 SkPaint* fPaint;
76 uint8_t fAlpha;
77 };
78
flatten(SkFlattenableWriteBuffer & buffer)79 void 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 */
setContext(const SkBitmap & device,const SkPaint & paint,const SkMatrix & matrix)91 bool 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
shadeSpan(int x,int y,SkPMColor result[],int count)116 void 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