1/* libs/graphics/animator/SkDrawGradient.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 "SkDrawGradient.h"
19#include "SkAnimateMaker.h"
20#include "SkAnimatorScript.h"
21#include "SkGradientShader.h"
22#include "SkUnitMapper.h"
23
24SkScalar SkUnitToScalar(U16CPU x) {
25#ifdef SK_SCALAR_IS_FLOAT
26    return x / 65535.0f;
27#else
28    return x + (x >> 8);
29#endif
30}
31
32U16CPU SkScalarToUnit(SkScalar x) {
33    SkScalar pin =  SkScalarPin(x, 0, SK_Scalar1);
34#ifdef SK_SCALAR_IS_FLOAT
35    return (int) (pin * 65535.0f);
36#else
37    return pin - (pin >= 32768);
38#endif
39}
40
41class SkGradientUnitMapper : public SkUnitMapper {
42public:
43    SkGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) {
44    }
45
46    // overrides for SkFlattenable
47    virtual Factory getFactory() { return NULL; }
48
49protected:
50    virtual uint16_t mapUnit16(uint16_t x) {
51        fUnit = SkUnitToScalar(x);
52        SkScriptValue value;
53        SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
54        engine.propertyCallBack(GetUnitValue, &fUnit);
55        if (engine.evaluate(fScript, &value, SkType_Float))
56            x = SkScalarToUnit(value.fOperand.fScalar);
57        return x;
58    }
59
60    static bool GetUnitValue(const char* token, size_t len, void* unitPtr, SkScriptValue* value) {
61        if (SK_LITERAL_STR_EQUAL("unit", token, len)) {
62            value->fOperand.fScalar = *(SkScalar*) unitPtr;
63            value->fType = SkType_Float;
64            return true;
65        }
66        return false;
67    }
68
69    SkAnimateMaker* fMaker;
70    const char* fScript;
71    SkScalar fUnit;
72};
73
74
75#if SK_USE_CONDENSED_INFO == 0
76
77const SkMemberInfo SkGradient::fInfo[] = {
78    SK_MEMBER_INHERITED,
79    SK_MEMBER_ARRAY(offsets, Float),
80    SK_MEMBER(unitMapper, String)
81};
82
83#endif
84
85DEFINE_GET_MEMBER(SkGradient);
86
87SkGradient::SkGradient() : fUnitMapper(NULL) {
88}
89
90SkGradient::~SkGradient() {
91    for (int index = 0; index < fDrawColors.count(); index++)
92        delete fDrawColors[index];
93    delete fUnitMapper;
94}
95
96bool SkGradient::add(SkAnimateMaker& , SkDisplayable* child) {
97    SkASSERT(child);
98    if (child->isColor()) {
99        SkDrawColor* color = (SkDrawColor*) child;
100        *fDrawColors.append() = color;
101        return true;
102    }
103    return false;
104}
105
106int SkGradient::addPrelude() {
107    int count = fDrawColors.count();
108    fColors.setCount(count);
109    for (int index = 0; index < count; index++)
110        fColors[index] = fDrawColors[index]->color;
111    return count;
112}
113
114#ifdef SK_DUMP_ENABLED
115void SkGradient::dumpRest(SkAnimateMaker* maker) {
116    dumpAttrs(maker);
117    //can a gradient have no colors?
118    bool closedYet = false;
119    SkDisplayList::fIndent += 4;
120    for (SkDrawColor** ptr = fDrawColors.begin(); ptr < fDrawColors.end(); ptr++) {
121        if (closedYet == false) {
122            SkDebugf(">\n");
123            closedYet = true;
124        }
125        SkDrawColor* color = *ptr;
126        color->dump(maker);
127    }
128    SkDisplayList::fIndent -= 4;
129    dumpChildren(maker, closedYet); //dumps the matrix if it has one
130}
131#endif
132
133void SkGradient::onEndElement(SkAnimateMaker& maker) {
134    if (offsets.count() != 0) {
135        if (offsets.count() != fDrawColors.count()) {
136            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsDontMatchColors);
137            return;
138        }
139        if (offsets[0] != 0) {
140            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustStartWithZero);
141            return;
142        }
143        if (offsets[offsets.count()-1] != SK_Scalar1) {
144            maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustEndWithOne);
145            return;
146        }
147        for (int i = 1; i < offsets.count(); i++) {
148            if (offsets[i] <= offsets[i-1]) {
149                maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustIncrease);
150                return;
151            }
152            if (offsets[i] > SK_Scalar1) {
153                maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustBeNoMoreThanOne);
154                return;
155            }
156        }
157    }
158    if (unitMapper.size() > 0)
159        fUnitMapper = new SkGradientUnitMapper(&maker, unitMapper.c_str());
160    INHERITED::onEndElement(maker);
161}
162
163#if SK_USE_CONDENSED_INFO == 0
164
165const SkMemberInfo SkLinearGradient::fInfo[] = {
166    SK_MEMBER_INHERITED,
167    SK_MEMBER_ARRAY(points, Float),
168};
169
170#endif
171
172DEFINE_GET_MEMBER(SkLinearGradient);
173
174SkLinearGradient::SkLinearGradient() {
175}
176
177void SkLinearGradient::onEndElement(SkAnimateMaker& maker)
178{
179    if (points.count() != 4)
180        maker.setErrorCode(SkDisplayXMLParserError::kGradientPointsLengthMustBeFour);
181    INHERITED::onEndElement(maker);
182}
183
184#ifdef SK_DUMP_ENABLED
185void SkLinearGradient::dump(SkAnimateMaker* maker) {
186    dumpBase(maker);
187    dumpRest(maker);
188    }
189#endif
190
191SkShader* SkLinearGradient::getShader() {
192    if (addPrelude() == 0 || points.count() != 4)
193        return NULL;
194    SkShader* shader = SkGradientShader::CreateLinear((SkPoint*)points.begin(),
195        fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
196    SkAutoTDelete<SkShader> autoDel(shader);
197    addPostlude(shader);
198    (void)autoDel.detach();
199    return shader;
200}
201
202
203#if SK_USE_CONDENSED_INFO == 0
204
205const SkMemberInfo SkRadialGradient::fInfo[] = {
206    SK_MEMBER_INHERITED,
207    SK_MEMBER(center, Point),
208    SK_MEMBER(radius, Float)
209};
210
211#endif
212
213DEFINE_GET_MEMBER(SkRadialGradient);
214
215SkRadialGradient::SkRadialGradient() : radius(0) {
216    center.set(0, 0);
217}
218
219#ifdef SK_DUMP_ENABLED
220void SkRadialGradient::dump(SkAnimateMaker* maker) {
221    dumpBase(maker);
222    dumpRest(maker);
223}
224#endif
225
226SkShader* SkRadialGradient::getShader() {
227    if (addPrelude() == 0)
228        return NULL;
229    SkShader* shader = SkGradientShader::CreateRadial(center,
230        radius, fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
231    SkAutoTDelete<SkShader> autoDel(shader);
232    addPostlude(shader);
233    (void)autoDel.detach();
234    return shader;
235}
236