SkComposeShader.cpp revision a0c2bc24381fea063008f9c8823756eb020603b3
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 "SkFlattenableBuffers.h"
15#include "SkXfermode.h"
16
17///////////////////////////////////////////////////////////////////////////////
18
19SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
20    fShaderA = sA;  sA->ref();
21    fShaderB = sB;  sB->ref();
22    // mode may be null
23    fMode = mode;
24    SkSafeRef(mode);
25}
26
27SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
28    INHERITED(buffer) {
29    fShaderA = buffer.readFlattenableT<SkShader>();
30    if (NULL == fShaderA) {
31        fShaderA = SkNEW_ARGS(SkColorShader, (0));
32    }
33    fShaderB = buffer.readFlattenableT<SkShader>();
34    if (NULL == fShaderB) {
35        fShaderB = SkNEW_ARGS(SkColorShader, (0));
36    }
37    fMode = buffer.readFlattenableT<SkXfermode>();
38}
39
40SkComposeShader::~SkComposeShader() {
41    SkSafeUnref(fMode);
42    fShaderB->unref();
43    fShaderA->unref();
44}
45
46void SkComposeShader::beginSession() {
47    this->INHERITED::beginSession();
48    fShaderA->beginSession();
49    fShaderB->beginSession();
50}
51
52void SkComposeShader::endSession() {
53    fShaderA->endSession();
54    fShaderB->endSession();
55    this->INHERITED::endSession();
56}
57
58class SkAutoAlphaRestore {
59public:
60    SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
61        fAlpha = paint->getAlpha();
62        fPaint = paint;
63        paint->setAlpha(newAlpha);
64    }
65
66    ~SkAutoAlphaRestore() {
67        fPaint->setAlpha(fAlpha);
68    }
69private:
70    SkPaint*    fPaint;
71    uint8_t     fAlpha;
72};
73
74void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) const {
75    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,
87                                 const SkMatrix& matrix) {
88    if (!this->INHERITED::setContext(device, paint, matrix)) {
89        return false;
90    }
91
92    // we preconcat our localMatrix (if any) with the device matrix
93    // before calling our sub-shaders
94
95    SkMatrix tmpM;
96
97    (void)this->getLocalMatrix(&tmpM);
98    tmpM.setConcat(matrix, tmpM);
99
100    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
110void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
111    SkShader*   shaderA = fShaderA;
112    SkShader*   shaderB = fShaderB;
113    SkXfermode* mode = fMode;
114    unsigned    scale = SkAlpha255To256(this->getPaintAlpha());
115
116    SkPMColor   tmp[TMP_COLOR_COUNT];
117
118    if (NULL == mode) {   // implied SRC_OVER
119        // TODO: when we have a good test-case, should use SkBlitRow::Proc32
120        // for these loops
121        do {
122            int n = count;
123            if (n > TMP_COLOR_COUNT) {
124                n = TMP_COLOR_COUNT;
125            }
126
127            shaderA->shadeSpan(x, y, result, n);
128            shaderB->shadeSpan(x, y, tmp, n);
129
130            if (256 == scale) {
131                for (int i = 0; i < n; i++) {
132                    result[i] = SkPMSrcOver(tmp[i], result[i]);
133                }
134            } else {
135                for (int i = 0; i < n; i++) {
136                    result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
137                                            scale);
138                }
139            }
140
141            result += n;
142            x += n;
143            count -= n;
144        } while (count > 0);
145    } else {    // use mode for the composition
146        do {
147            int n = count;
148            if (n > TMP_COLOR_COUNT) {
149                n = TMP_COLOR_COUNT;
150            }
151
152            shaderA->shadeSpan(x, y, result, n);
153            shaderB->shadeSpan(x, y, tmp, n);
154            mode->xfer32(result, tmp, n, NULL);
155
156            if (256 == scale) {
157                for (int i = 0; i < n; i++) {
158                    result[i] = SkAlphaMulQ(result[i], scale);
159                }
160            }
161
162            result += n;
163            x += n;
164            count -= n;
165        } while (count > 0);
166    }
167}
168