blob: 62bb2d6fdcb11afaf5161f4c02d217f7736c63ef [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/effects/SkShaderExtras.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
reed@google.com82065d62011-02-07 15:30:46 +00005** 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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.com82065d62011-02-07 15:30:46 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.com82065d62011-02-07 15:30:46 +000011** 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
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** limitations under the License.
16*/
17
18#include "SkComposeShader.h"
19#include "SkColorFilter.h"
20#include "SkColorPriv.h"
21#include "SkXfermode.h"
22
reed@google.com82065d62011-02-07 15:30:46 +000023///////////////////////////////////////////////////////////////////////////////
24
25SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000026 fShaderA = sA; sA->ref();
27 fShaderB = sB; sB->ref();
28 // mode may be null
reed@google.com82065d62011-02-07 15:30:46 +000029 fMode = mode;
30 SkSafeRef(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +000031}
32
33SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
reed@google.com82065d62011-02-07 15:30:46 +000034 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000035 fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
36 fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
37 fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
38}
39
reed@google.com82065d62011-02-07 15:30:46 +000040SkComposeShader::~SkComposeShader() {
41 SkSafeUnref(fMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +000042 fShaderB->unref();
43 fShaderA->unref();
44}
45
reed@google.com82065d62011-02-07 15:30:46 +000046void SkComposeShader::beginSession() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000047 this->INHERITED::beginSession();
48 fShaderA->beginSession();
49 fShaderB->beginSession();
50}
51
reed@google.com82065d62011-02-07 15:30:46 +000052void SkComposeShader::endSession() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000053 fShaderA->endSession();
54 fShaderB->endSession();
55 this->INHERITED::endSession();
56}
57
58class SkAutoAlphaRestore {
59public:
reed@google.com82065d62011-02-07 15:30:46 +000060 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000061 fAlpha = paint->getAlpha();
62 fPaint = paint;
63 paint->setAlpha(newAlpha);
64 }
reed@google.com82065d62011-02-07 15:30:46 +000065
66 ~SkAutoAlphaRestore() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000067 fPaint->setAlpha(fAlpha);
68 }
69private:
70 SkPaint* fPaint;
71 uint8_t fAlpha;
72};
73
reed@google.com82065d62011-02-07 15:30:46 +000074void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 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,
reed@google.com82065d62011-02-07 15:30:46 +000087 const SkMatrix& matrix) {
88 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 return false;
reed@google.com82065d62011-02-07 15:30:46 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
92 // we preconcat our localMatrix (if any) with the device matrix
93 // before calling our sub-shaders
94
95 SkMatrix tmpM;
reed@google.com82065d62011-02-07 15:30:46 +000096
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 (void)this->getLocalMatrix(&tmpM);
98 tmpM.setConcat(matrix, tmpM);
reed@google.com82065d62011-02-07 15:30:46 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 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
reed@google.com82065d62011-02-07 15:30:46 +0000110void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 SkShader* shaderA = fShaderA;
112 SkShader* shaderB = fShaderB;
113 SkXfermode* mode = fMode;
114 unsigned scale = SkAlpha255To256(this->getPaintAlpha());
reed@google.com82065d62011-02-07 15:30:46 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 SkPMColor tmp[TMP_COLOR_COUNT];
117
reed@google.com82065d62011-02-07 15:30:46 +0000118 if (NULL == mode) { // implied SRC_OVER
reed@android.comc4cae852009-09-23 15:06:10 +0000119 // TODO: when we have a good test-case, should use SkBlitRow::Proc32
120 // for these loops
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 do {
122 int n = count;
reed@google.com82065d62011-02-07 15:30:46 +0000123 if (n > TMP_COLOR_COUNT) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 n = TMP_COLOR_COUNT;
reed@google.com82065d62011-02-07 15:30:46 +0000125 }
126
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 shaderA->shadeSpan(x, y, result, n);
128 shaderB->shadeSpan(x, y, tmp, n);
129
reed@google.com82065d62011-02-07 15:30:46 +0000130 if (256 == scale) {
131 for (int i = 0; i < n; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 result[i] = SkPMSrcOver(tmp[i], result[i]);
reed@google.com82065d62011-02-07 15:30:46 +0000133 }
134 } else {
135 for (int i = 0; i < n; i++) {
136 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
137 scale);
138 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 }
reed@google.com82065d62011-02-07 15:30:46 +0000140
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 result += n;
142 x += n;
143 count -= n;
144 } while (count > 0);
reed@google.com82065d62011-02-07 15:30:46 +0000145 } else { // use mode for the composition
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 do {
147 int n = count;
reed@google.com82065d62011-02-07 15:30:46 +0000148 if (n > TMP_COLOR_COUNT) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 n = TMP_COLOR_COUNT;
reed@google.com82065d62011-02-07 15:30:46 +0000150 }
151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 shaderA->shadeSpan(x, y, result, n);
153 shaderB->shadeSpan(x, y, tmp, n);
154 mode->xfer32(result, tmp, n, NULL);
155
reed@google.com82065d62011-02-07 15:30:46 +0000156 if (256 == scale) {
157 for (int i = 0; i < n; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 result[i] = SkAlphaMulQ(result[i], scale);
reed@google.com82065d62011-02-07 15:30:46 +0000159 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 }
161
162 result += n;
163 x += n;
164 count -= n;
165 } while (count > 0);
166 }
167}
reed@google.com82065d62011-02-07 15:30:46 +0000168