/*
* Copyright 2006 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkDisplayable.h"
#include "SkDisplayApply.h"
#include "SkParse.h"
#ifdef SK_DEBUG
#include "SkDisplayList.h"
#endif
#include "SkDisplayTypes.h"
#ifdef SK_FIND_LEAKS
// int SkDisplayable::fAllocationCount;
SkTDDisplayableArray SkDisplayable::fAllocations;
#endif
#ifdef SK_DEBUG
SkDisplayable::SkDisplayable() {
id = _id.c_str();
#ifdef SK_FIND_LEAKS
// fAllocationCount++;
*fAllocations.append() = this;
#endif
}
#endif
SkDisplayable::~SkDisplayable() {
#ifdef SK_FIND_LEAKS
// fAllocationCount--;
int index = fAllocations.find(this);
SkASSERT(index >= 0);
fAllocations.remove(index);
#endif
}
bool SkDisplayable::add(SkAnimateMaker& , SkDisplayable* child) {
return false;
}
//void SkDisplayable::apply(SkAnimateMaker& , const SkMemberInfo* ,
// SkDisplayable* , SkScalar [], int count) {
// SkASSERT(0);
//}
bool SkDisplayable::canContainDependents() const {
return false;
}
bool SkDisplayable::childrenNeedDisposing() const {
return false;
}
void SkDisplayable::clearBounder() {
}
bool SkDisplayable::contains(SkDisplayable* ) {
return false;
}
SkDisplayable* SkDisplayable::contains(const SkString& ) {
return NULL;
}
SkDisplayable* SkDisplayable::deepCopy(SkAnimateMaker* maker) {
SkDisplayTypes type = getType();
if (type == SkType_Unknown) {
SkASSERT(0);
return NULL;
}
SkDisplayable* copy = SkDisplayType::CreateInstance(maker, type);
int index = -1;
int propIndex = 0;
const SkMemberInfo* info;
do {
info = copy->getMember(++index);
if (info == NULL)
break;
if (info->fType == SkType_MemberProperty) {
SkScriptValue value;
if (getProperty(propIndex, &value))
copy->setProperty(propIndex, value);
propIndex++;
continue;
}
if (info->fType == SkType_MemberFunction)
continue;
if (info->fType == SkType_Array) {
SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this);
int arrayCount;
if (array == NULL || (arrayCount = array->count()) == 0)
continue;
SkTDOperandArray* copyArray = (SkTDOperandArray*) info->memberData(copy);
copyArray->setCount(arrayCount);
SkDisplayTypes elementType;
if (type == SkType_Array) {
SkDisplayArray* dispArray = (SkDisplayArray*) this;
elementType = dispArray->values.getType();
} else
elementType = info->arrayType();
size_t elementSize = SkMemberInfo::GetSize(elementType);
size_t byteSize = elementSize * arrayCount;
memcpy(copyArray->begin(), array->begin(), byteSize);
continue;
}
if (SkDisplayType::IsDisplayable(maker, info->fType)) {
SkDisplayable** displayable = (SkDisplayable**) info->memberData(this);
if (*displayable == NULL || *displayable == (SkDisplayable*) -1)
continue;
SkDisplayable* deeper = (*displayable)->deepCopy(maker);
info->setMemberData(copy, deeper, sizeof(deeper));
continue;
}
if (info->fType == SkType_String || info->fType == SkType_DynamicString) {
SkString* string;
info->getString(this, &string);
info->setString(copy, string);
continue;
}
void* data = info->memberData(this);
size_t size = SkMemberInfo::GetSize(info->fType);
info->setMemberData(copy, data, size);
} while (true);
copy->dirty();
return copy;
}
void SkDisplayable::dirty() {
}
#ifdef SK_DUMP_ENABLED
void SkDisplayable::dump(SkAnimateMaker* maker) {
dumpBase(maker);
#if SK_USE_CONDENSED_INFO == 0
this->dumpAttrs(maker);
this->dumpChildren(maker);
#endif
}
void SkDisplayable::dumpAttrs(SkAnimateMaker* maker) {
SkDisplayTypes type = getType();
if (type == SkType_Unknown) {
//SkDebugf("/>\n");
return;
}
SkDisplayable* blankCopy = SkDisplayType::CreateInstance(maker, type);
int index = -1;
int propIndex = 0;
const SkMemberInfo* info;
const SkMemberInfo* blankInfo;
SkScriptValue value;
SkScriptValue blankValue;
SkOperand values[2];
SkOperand blankValues[2];
do {
info = this->getMember(++index);
if (NULL == info) {
//SkDebugf("\n");
break;
}
if (SkType_MemberProperty == info->fType) {
if (getProperty(propIndex, &value)) {
blankCopy->getProperty(propIndex, &blankValue);
//last two are dummies
dumpValues(info, value.fType, value.fOperand, blankValue.fOperand, value.fOperand, blankValue.fOperand);
}
propIndex++;
continue;
}
if (SkDisplayType::IsDisplayable(maker, info->fType)) {
continue;
}
if (info->fType == SkType_MemberFunction)
continue;
if (info->fType == SkType_Array) {
SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this);
int arrayCount;
if (array == NULL || (arrayCount = array->count()) == 0)
continue;
SkDisplayTypes elementType;
if (type == SkType_Array) {
SkDisplayArray* dispArray = (SkDisplayArray*) this;
elementType = dispArray->values.getType();
} else
elementType = info->arrayType();
bool firstElem = true;
SkDebugf("%s=\"[", info->fName);
for (SkOperand* op = array->begin(); op < array->end(); op++) {
if (!firstElem) SkDebugf(",");
switch (elementType) {
case SkType_Displayable:
SkDebugf("%s", op->fDisplayable->id);
break;
case SkType_Int:
SkDebugf("%d", op->fS32);
break;
case SkType_Float:
#ifdef SK_CAN_USE_FLOAT
SkDebugf("%g", SkScalarToFloat(op->fScalar));
#else
SkDebugf("%x", op->fScalar);
#endif
break;
case SkType_String:
case SkType_DynamicString:
SkDebugf("%s", op->fString->c_str());
break;
default:
break;
}
firstElem = false;
}
SkDebugf("]\" ");
continue;
}
if (info->fType == SkType_String || info->fType == SkType_DynamicString) {
SkString* string;
info->getString(this, &string);
if (string->isEmpty() == false)
SkDebugf("%s=\"%s\"\t", info->fName, string->c_str());
continue;
}
blankInfo = blankCopy->getMember(index);
int i = info->fCount;
info->getValue(this, values, i);
blankInfo->getValue(blankCopy, blankValues, i);
dumpValues(info, info->fType, values[0], blankValues[0], values[1], blankValues[1]);
} while (true);
delete blankCopy;
}
void SkDisplayable::dumpBase(SkAnimateMaker* maker) {
SkDisplayTypes type = getType();
const char* elementName = "(unknown)";
if (type != SkType_Unknown && type != SkType_Screenplay)
elementName = SkDisplayType::GetName(maker, type);
SkDebugf("%*s", SkDisplayList::fIndent, "");
if (SkDisplayList::fDumpIndex != 0 && SkDisplayList::fIndent == 0)
SkDebugf("%d: ", SkDisplayList::fDumpIndex);
SkDebugf("<%s ", elementName);
if (strcmp(id,"") != 0)
SkDebugf("id=\"%s\" ", id);
}
void SkDisplayable::dumpChildren(SkAnimateMaker* maker, bool closedAngle) {
int index = -1;
const SkMemberInfo* info;
index = -1;
SkDisplayList::fIndent += 4;
do {
info = this->getMember(++index);
if (NULL == info) {
break;
}
if (SkDisplayType::IsDisplayable(maker, info->fType)) {
SkDisplayable** displayable = (SkDisplayable**) info->memberData(this);
if (*displayable == NULL || *displayable == (SkDisplayable*) -1)
continue;
if (closedAngle == false) {
SkDebugf(">\n");
closedAngle = true;
}
(*displayable)->dump(maker);
}
} while (true);
SkDisplayList::fIndent -= 4;
if (closedAngle)
dumpEnd(maker);
else
SkDebugf("/>\n");
}
void SkDisplayable::dumpEnd(SkAnimateMaker* maker) {
SkDisplayTypes type = getType();
const char* elementName = "(unknown)";
if (type != SkType_Unknown && type != SkType_Screenplay)
elementName = SkDisplayType::GetName(maker, type);
SkDebugf("%*s", SkDisplayList::fIndent, "");
SkDebugf("</%s>\n", elementName);
}
void SkDisplayable::dumpEvents() {
}
void SkDisplayable::dumpValues(const SkMemberInfo* info, SkDisplayTypes type, SkOperand op, SkOperand blankOp,
SkOperand op2, SkOperand blankOp2) {
switch (type) {
case SkType_BitmapEncoding:
switch (op.fS32) {
case 0 : SkDebugf("type=\"jpeg\" ");
break;
case 1 : SkDebugf("type=\"png\" ");
break;
default: SkDebugf("type=\"UNDEFINED\" ");
}
break;
//should make this a separate case in dump attrs, rather than make dump values have a larger signature
case SkType_Point:
if (op.fScalar != blankOp.fScalar || op2.fScalar != blankOp.fScalar) {
#ifdef SK_CAN_USE_FLOAT
SkDebugf("%s=\"[%g,%g]\" ", info->fName, SkScalarToFloat(op.fScalar), SkScalarToFloat(op2.fScalar));
#else
SkDebugf("%s=\"[%x,%x]\" ", info->fName, op.fScalar, op2.fScalar);
#endif
}
break;
case SkType_FromPathMode:
switch (op.fS32) {
case 0:
//don't want to print anything for 0, just adding it to remove it from default:
break;
case 1:
SkDebugf("%s=\"%s\" ", info->fName, "angle");
break;
case 2:
SkDebugf("%s=\"%s\" ", info->fName, "position");
break;
default:
SkDebugf("%s=\"INVALID\" ", info->fName);
}
break;
case SkType_MaskFilterBlurStyle:
switch (op.fS32) {
case 0:
break;
case 1:
SkDebugf("%s=\"%s\" ", info->fName, "solid");
break;
case 2:
SkDebugf("%s=\"%s\" ", info->fName, "outer");
break;
case 3:
SkDebugf("%s=\"%s\" ", info->fName, "inner");
break;
default:
SkDebugf("%s=\"INVALID\" ", info->fName);
}
break;
case SkType_FilterType:
if (op.fS32 == 1)
SkDebugf("%s=\"%s\" ", info->fName, "bilinear");
break;
case SkType_PathDirection:
SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "cw" : "ccw");
break;
case SkType_FillType:
SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "winding" : "evenOdd");
break;
case SkType_TileMode:
//correct to look at the S32?
if (op.fS32 != blankOp.fS32)
SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "clamp" : op.fS32 == 1 ? "repeat" : "mirror");
break;
case SkType_Boolean:
if (op.fS32 != blankOp.fS32)
SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "false" : "true");
break;
case SkType_Int:
if (op.fS32 != blankOp.fS32)
SkDebugf(" %s=\"%d\" ", info->fName, op.fS32);
break;
case SkType_Float:
if (op.fScalar != blankOp.fScalar) { //or /65536?
#ifdef SK_CAN_USE_FLOAT
SkDebugf("%s=\"%g\" ", info->fName, SkScalarToFloat(op.fScalar));
#else
SkDebugf("%s=\"%x\" ", info->fName, op.fScalar);
#endif
}
break;
case SkType_String:
case SkType_DynamicString:
if (op.fString->size() > 0)
SkDebugf("%s=\"%s\" ", info->fName, op.fString->c_str());
break;
case SkType_MSec:
if (op.fS32 != blankOp.fS32) {
#ifdef SK_CAN_USE_FLOAT
SkDebugf(" %s=\"%g\" ", info->fName, SkScalarToFloat(SkScalarDiv(op.fS32, 1000)));
#else
SkDebugf(" %s=\"%x\" ", info->fName, SkScalarDiv(op.fS32, 1000));
#endif
}
default:
SkDebugf("");
}
}
#endif
bool SkDisplayable::enable( SkAnimateMaker& ) {
return false;
}
void SkDisplayable::enableBounder() {
}
void SkDisplayable::executeFunction(SkDisplayable* , int index,
SkTDArray<SkScriptValue>& , SkDisplayTypes, SkScriptValue* ) {
SkASSERT(0);
}
void SkDisplayable::executeFunction(SkDisplayable* target,
const SkMemberInfo* info, SkTypedArray* values, SkScriptValue* value) {
SkTDArray<SkScriptValue> typedValues;
for (SkOperand* op = values->begin(); op < values->end(); op++) {
SkScriptValue temp;
temp.fType = values->getType();
temp.fOperand = *op;
*typedValues.append() = temp;
}
executeFunction(target, info->functionIndex(), typedValues, info->getType(), value);
}
void SkDisplayable::executeFunction2(SkDisplayable* , int index,
SkOpArray* params, SkDisplayTypes, SkOperand2* ) {
SkASSERT(0);
}
void SkDisplayable::getBounds(SkRect* rect) {
SkASSERT(rect);
rect->fLeft = rect->fTop = SK_ScalarMax;
rect->fRight= rect->fBottom = -SK_ScalarMax;
}
const SkFunctionParamType* SkDisplayable::getFunctionsParameters() {
return NULL;
}
const SkMemberInfo* SkDisplayable::getMember(int index) {
return NULL;
}
const SkMemberInfo* SkDisplayable::getMember(const char name[]) {
return NULL;
}
const SkFunctionParamType* SkDisplayable::getParameters(const SkMemberInfo* info,
int* paramCount) {
const SkFunctionParamType* params = getFunctionsParameters();
SkASSERT(params != NULL);
int funcIndex = info->functionIndex();
// !!! eventually break traversing params into an external function (maybe this whole function)
int index = funcIndex;
int offset = 0;
while (--index >= 0) {
while (params[offset] != 0)
offset++;
offset++;
}
int count = 0;
while (params[offset] != 0) {
count++;
offset++;
}
*paramCount = count;
return ¶ms[offset - count];
}
SkDisplayable* SkDisplayable::getParent() const {
return NULL;
}
bool SkDisplayable::getProperty(int index, SkScriptValue* ) const {
// SkASSERT(0);
return false;
}
bool SkDisplayable::getProperty2(int index, SkOperand2* value) const {
SkASSERT(0);
return false;
}
SkDisplayTypes SkDisplayable::getType() const {
return SkType_Unknown;
}
bool SkDisplayable::hasEnable() const {
return false;
}
bool SkDisplayable::isDrawable() const {
return false;
}
void SkDisplayable::onEndElement(SkAnimateMaker& ) {}
const SkMemberInfo* SkDisplayable::preferredChild(SkDisplayTypes type) {
return NULL;
}
bool SkDisplayable::resolveIDs(SkAnimateMaker& maker, SkDisplayable* original, SkApply* apply) {
return false;
}
//SkDisplayable* SkDisplayable::resolveTarget(SkAnimateMaker& ) {
// return this;
//}
void SkDisplayable::setChildHasID() {
}
bool SkDisplayable::setParent(SkDisplayable* ) {
return false;
}
bool SkDisplayable::setProperty(int index, SkScriptValue& ) {
//SkASSERT(0);
return false;
}
void SkDisplayable::setReference(const SkMemberInfo* info, SkDisplayable* displayable) {
if (info->fType == SkType_MemberProperty) {
SkScriptValue scriptValue;
scriptValue.fOperand.fDisplayable = displayable;
scriptValue.fType = displayable->getType();
setProperty(info->propertyIndex(), scriptValue);
} else if (info->fType == SkType_Array) {
SkASSERT(displayable->getType() == SkType_Array);
SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
SkTDScalarArray* array = (SkTDScalarArray* ) info->memberData(this);
array->setCount(dispArray->values.count());
memcpy(array->begin(), dispArray->values.begin(), dispArray->values.count() * sizeof(int));
//
// !!! need a way for interpreter engine to own array
// !!! probably need to replace all scriptable arrays with single bigger array
// that has operand and type on every element -- or
// when array is dirtied, need to get parent to reparse to local array
} else {
void* storage = info->memberData(this);
memcpy(storage, &displayable, sizeof(SkDisplayable*));
}
// !!! unclear why displayable is dirtied here
// if this is called, this breaks fromPath.xml
// displayable->dirty();
}
#ifdef SK_DEBUG
void SkDisplayable::validate() {
}
#endif