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