blob: c8d329914edc38bbab82195c241a6b7dfb32bf0c [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkComposeShader.h"
11#include "SkColorFilter.h"
12#include "SkColorPriv.h"
reed@google.com573f22b2011-11-30 19:17:15 +000013#include "SkColorShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkXfermode.h"
15
reed@google.com82065d62011-02-07 15:30:46 +000016///////////////////////////////////////////////////////////////////////////////
17
18SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000019 fShaderA = sA; sA->ref();
20 fShaderB = sB; sB->ref();
21 // mode may be null
reed@google.com82065d62011-02-07 15:30:46 +000022 fMode = mode;
23 SkSafeRef(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +000024}
25
26SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
reed@google.com82065d62011-02-07 15:30:46 +000027 INHERITED(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000028 fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
reed@google.com573f22b2011-11-30 19:17:15 +000029 if (NULL == fShaderA) {
30 fShaderA = SkNEW_ARGS(SkColorShader, (0));
31 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
reed@google.com573f22b2011-11-30 19:17:15 +000033 if (NULL == fShaderB) {
34 fShaderB = SkNEW_ARGS(SkColorShader, (0));
35 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000036 fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
37}
38
reed@google.com82065d62011-02-07 15:30:46 +000039SkComposeShader::~SkComposeShader() {
40 SkSafeUnref(fMode);
reed@android.com8a1c16f2008-12-17 15:59:43 +000041 fShaderB->unref();
42 fShaderA->unref();
43}
44
reed@google.com82065d62011-02-07 15:30:46 +000045void SkComposeShader::beginSession() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000046 this->INHERITED::beginSession();
47 fShaderA->beginSession();
48 fShaderB->beginSession();
49}
50
reed@google.com82065d62011-02-07 15:30:46 +000051void SkComposeShader::endSession() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 fShaderA->endSession();
53 fShaderB->endSession();
54 this->INHERITED::endSession();
55}
56
57class SkAutoAlphaRestore {
58public:
reed@google.com82065d62011-02-07 15:30:46 +000059 SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 fAlpha = paint->getAlpha();
61 fPaint = paint;
62 paint->setAlpha(newAlpha);
63 }
reed@google.com82065d62011-02-07 15:30:46 +000064
65 ~SkAutoAlphaRestore() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 fPaint->setAlpha(fAlpha);
67 }
68private:
69 SkPaint* fPaint;
70 uint8_t fAlpha;
71};
72
reed@google.com82065d62011-02-07 15:30:46 +000073void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 this->INHERITED::flatten(buffer);
75 buffer.writeFlattenable(fShaderA);
76 buffer.writeFlattenable(fShaderB);
77 buffer.writeFlattenable(fMode);
78}
79
80/* We call setContext on our two worker shaders. However, we
81 always let them see opaque alpha, and if the paint really
82 is translucent, then we apply that after the fact.
83*/
84bool SkComposeShader::setContext(const SkBitmap& device,
85 const SkPaint& paint,
reed@google.com82065d62011-02-07 15:30:46 +000086 const SkMatrix& matrix) {
87 if (!this->INHERITED::setContext(device, paint, matrix)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 return false;
reed@google.com82065d62011-02-07 15:30:46 +000089 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000090
91 // we preconcat our localMatrix (if any) with the device matrix
92 // before calling our sub-shaders
93
94 SkMatrix tmpM;
reed@google.com82065d62011-02-07 15:30:46 +000095
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 (void)this->getLocalMatrix(&tmpM);
97 tmpM.setConcat(matrix, tmpM);
reed@google.com82065d62011-02-07 15:30:46 +000098
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 SkAutoAlphaRestore restore(const_cast<SkPaint*>(&paint), 0xFF);
100
101 return fShaderA->setContext(device, paint, tmpM) &&
102 fShaderB->setContext(device, paint, tmpM);
103}
104
105// larger is better (fewer times we have to loop), but we shouldn't
106// take up too much stack-space (each element is 4 bytes)
107#define TMP_COLOR_COUNT 64
108
reed@google.com82065d62011-02-07 15:30:46 +0000109void SkComposeShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 SkShader* shaderA = fShaderA;
111 SkShader* shaderB = fShaderB;
112 SkXfermode* mode = fMode;
113 unsigned scale = SkAlpha255To256(this->getPaintAlpha());
reed@google.com82065d62011-02-07 15:30:46 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 SkPMColor tmp[TMP_COLOR_COUNT];
116
reed@google.com82065d62011-02-07 15:30:46 +0000117 if (NULL == mode) { // implied SRC_OVER
reed@android.comc4cae852009-09-23 15:06:10 +0000118 // TODO: when we have a good test-case, should use SkBlitRow::Proc32
119 // for these loops
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 do {
121 int n = count;
reed@google.com82065d62011-02-07 15:30:46 +0000122 if (n > TMP_COLOR_COUNT) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 n = TMP_COLOR_COUNT;
reed@google.com82065d62011-02-07 15:30:46 +0000124 }
125
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 shaderA->shadeSpan(x, y, result, n);
127 shaderB->shadeSpan(x, y, tmp, n);
128
reed@google.com82065d62011-02-07 15:30:46 +0000129 if (256 == scale) {
130 for (int i = 0; i < n; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 result[i] = SkPMSrcOver(tmp[i], result[i]);
reed@google.com82065d62011-02-07 15:30:46 +0000132 }
133 } else {
134 for (int i = 0; i < n; i++) {
135 result[i] = SkAlphaMulQ(SkPMSrcOver(tmp[i], result[i]),
136 scale);
137 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 }
reed@google.com82065d62011-02-07 15:30:46 +0000139
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 result += n;
141 x += n;
142 count -= n;
143 } while (count > 0);
reed@google.com82065d62011-02-07 15:30:46 +0000144 } else { // use mode for the composition
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 do {
146 int n = count;
reed@google.com82065d62011-02-07 15:30:46 +0000147 if (n > TMP_COLOR_COUNT) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 n = TMP_COLOR_COUNT;
reed@google.com82065d62011-02-07 15:30:46 +0000149 }
150
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 shaderA->shadeSpan(x, y, result, n);
152 shaderB->shadeSpan(x, y, tmp, n);
153 mode->xfer32(result, tmp, n, NULL);
154
reed@google.com82065d62011-02-07 15:30:46 +0000155 if (256 == scale) {
156 for (int i = 0; i < n; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 result[i] = SkAlphaMulQ(result[i], scale);
reed@google.com82065d62011-02-07 15:30:46 +0000158 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 }
160
161 result += n;
162 x += n;
163 count -= n;
164 } while (count > 0);
165 }
166}
reed@google.com82065d62011-02-07 15:30:46 +0000167