/* libs/graphics/animator/SkDrawGradient.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include "SkDrawGradient.h"
#include "SkAnimateMaker.h"
#include "SkAnimatorScript.h"
#include "SkGradientShader.h"
#include "SkUnitMapper.h"
SkScalar SkUnitToScalar(U16CPU x) {
#ifdef SK_SCALAR_IS_FLOAT
return x / 65535.0f;
#else
return x + (x >> 8);
#endif
}
U16CPU SkScalarToUnit(SkScalar x) {
SkScalar pin = SkScalarPin(x, 0, SK_Scalar1);
#ifdef SK_SCALAR_IS_FLOAT
return (int) (pin * 65535.0f);
#else
return pin - (pin >= 32768);
#endif
}
class SkGradientUnitMapper : public SkUnitMapper {
public:
SkGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) {
}
// overrides for SkFlattenable
virtual Factory getFactory() { return NULL; }
protected:
virtual uint16_t mapUnit16(uint16_t x) {
fUnit = SkUnitToScalar(x);
SkScriptValue value;
SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
engine.propertyCallBack(GetUnitValue, &fUnit);
if (engine.evaluate(fScript, &value, SkType_Float))
x = SkScalarToUnit(value.fOperand.fScalar);
return x;
}
static bool GetUnitValue(const char* token, size_t len, void* unitPtr, SkScriptValue* value) {
if (SK_LITERAL_STR_EQUAL("unit", token, len)) {
value->fOperand.fScalar = *(SkScalar*) unitPtr;
value->fType = SkType_Float;
return true;
}
return false;
}
SkAnimateMaker* fMaker;
const char* fScript;
SkScalar fUnit;
};
#if SK_USE_CONDENSED_INFO == 0
const SkMemberInfo SkGradient::fInfo[] = {
SK_MEMBER_INHERITED,
SK_MEMBER_ARRAY(offsets, Float),
SK_MEMBER(unitMapper, String)
};
#endif
DEFINE_GET_MEMBER(SkGradient);
SkGradient::SkGradient() : fUnitMapper(NULL) {
}
SkGradient::~SkGradient() {
for (int index = 0; index < fDrawColors.count(); index++)
delete fDrawColors[index];
delete fUnitMapper;
}
bool SkGradient::add(SkAnimateMaker& , SkDisplayable* child) {
SkASSERT(child);
if (child->isColor()) {
SkDrawColor* color = (SkDrawColor*) child;
*fDrawColors.append() = color;
return true;
}
return false;
}
int SkGradient::addPrelude() {
int count = fDrawColors.count();
fColors.setCount(count);
for (int index = 0; index < count; index++)
fColors[index] = fDrawColors[index]->color;
return count;
}
#ifdef SK_DUMP_ENABLED
void SkGradient::dumpRest(SkAnimateMaker* maker) {
dumpAttrs(maker);
//can a gradient have no colors?
bool closedYet = false;
SkDisplayList::fIndent += 4;
for (SkDrawColor** ptr = fDrawColors.begin(); ptr < fDrawColors.end(); ptr++) {
if (closedYet == false) {
SkDebugf(">\n");
closedYet = true;
}
SkDrawColor* color = *ptr;
color->dump(maker);
}
SkDisplayList::fIndent -= 4;
dumpChildren(maker, closedYet); //dumps the matrix if it has one
}
#endif
void SkGradient::onEndElement(SkAnimateMaker& maker) {
if (offsets.count() != 0) {
if (offsets.count() != fDrawColors.count()) {
maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsDontMatchColors);
return;
}
if (offsets[0] != 0) {
maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustStartWithZero);
return;
}
if (offsets[offsets.count()-1] != SK_Scalar1) {
maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustEndWithOne);
return;
}
for (int i = 1; i < offsets.count(); i++) {
if (offsets[i] <= offsets[i-1]) {
maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustIncrease);
return;
}
if (offsets[i] > SK_Scalar1) {
maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsMustBeNoMoreThanOne);
return;
}
}
}
if (unitMapper.size() > 0)
fUnitMapper = new SkGradientUnitMapper(&maker, unitMapper.c_str());
INHERITED::onEndElement(maker);
}
#if SK_USE_CONDENSED_INFO == 0
const SkMemberInfo SkLinearGradient::fInfo[] = {
SK_MEMBER_INHERITED,
SK_MEMBER_ARRAY(points, Float),
};
#endif
DEFINE_GET_MEMBER(SkLinearGradient);
SkLinearGradient::SkLinearGradient() {
}
void SkLinearGradient::onEndElement(SkAnimateMaker& maker)
{
if (points.count() != 4)
maker.setErrorCode(SkDisplayXMLParserError::kGradientPointsLengthMustBeFour);
INHERITED::onEndElement(maker);
}
#ifdef SK_DUMP_ENABLED
void SkLinearGradient::dump(SkAnimateMaker* maker) {
dumpBase(maker);
dumpRest(maker);
}
#endif
SkShader* SkLinearGradient::getShader() {
if (addPrelude() == 0 || points.count() != 4)
return NULL;
SkShader* shader = SkGradientShader::CreateLinear((SkPoint*)points.begin(),
fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
SkAutoTDelete<SkShader> autoDel(shader);
addPostlude(shader);
(void)autoDel.detach();
return shader;
}
#if SK_USE_CONDENSED_INFO == 0
const SkMemberInfo SkRadialGradient::fInfo[] = {
SK_MEMBER_INHERITED,
SK_MEMBER(center, Point),
SK_MEMBER(radius, Float)
};
#endif
DEFINE_GET_MEMBER(SkRadialGradient);
SkRadialGradient::SkRadialGradient() : radius(0) {
center.set(0, 0);
}
#ifdef SK_DUMP_ENABLED
void SkRadialGradient::dump(SkAnimateMaker* maker) {
dumpBase(maker);
dumpRest(maker);
}
#endif
SkShader* SkRadialGradient::getShader() {
if (addPrelude() == 0)
return NULL;
SkShader* shader = SkGradientShader::CreateRadial(center,
radius, fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, fUnitMapper);
SkAutoTDelete<SkShader> autoDel(shader);
addPostlude(shader);
(void)autoDel.detach();
return shader;
}