/* libs/graphics/animator/SkMemberInfo.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 "SkMemberInfo.h"
#include "SkAnimateMaker.h"
#include "SkAnimatorScript.h"
#include "SkBase64.h"
#include "SkCamera.h"
#include "SkDisplayable.h"
#include "SkDisplayTypes.h"
#include "SkDraw3D.h"
#include "SkDrawColor.h"
#include "SkParse.h"
#include "SkScript.h"
#include "SkTSearch.h"
#include "SkTypedArray.h"
size_t SkMemberInfo::GetSize(SkDisplayTypes type) { // size of simple types only
size_t byteSize;
switch (type) {
case SkType_ARGB:
byteSize = sizeof(SkColor);
break;
case SkType_AddMode:
case SkType_Align:
case SkType_ApplyMode:
case SkType_ApplyTransition:
case SkType_BitmapEncoding:
case SkType_Boolean:
case SkType_Cap:
case SkType_EventCode:
case SkType_EventKind:
case SkType_EventMode:
case SkType_FilterType:
case SkType_FontStyle:
case SkType_FromPathMode:
case SkType_Join:
case SkType_MaskFilterBlurStyle:
case SkType_PathDirection:
case SkType_Style:
case SkType_TileMode:
case SkType_Xfermode:
byteSize = sizeof(int);
break;
case SkType_Base64: // assume base64 data is always const, copied by ref
case SkType_Displayable:
case SkType_Drawable:
case SkType_Matrix:
byteSize = sizeof(void*);
break;
case SkType_MSec:
byteSize = sizeof(SkMSec);
break;
case SkType_Point:
byteSize = sizeof(SkPoint);
break;
case SkType_3D_Point:
byteSize = sizeof(Sk3D_Point);
break;
case SkType_Int:
byteSize = sizeof(int32_t);
break;
case SkType_Float:
byteSize = sizeof(SkScalar);
break;
case SkType_DynamicString:
case SkType_String:
byteSize = sizeof(SkString); // assume we'll copy by reference, not value
break;
default:
// SkASSERT(0);
byteSize = 0;
}
return byteSize;
}
bool SkMemberInfo::getArrayValue(const SkDisplayable* displayable, int index, SkOperand* value) const {
SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
char* valuePtr = (char*) *(SkOperand**) memberData(displayable);
SkDisplayTypes type = (SkDisplayTypes) 0;
if (displayable->getType() == SkType_Array) {
SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
if (dispArray->values.count() <= index)
return false;
type = dispArray->values.getType();
} else
SkASSERT(0); // incomplete
size_t byteSize = GetSize(type);
memcpy(value, valuePtr + index * byteSize, byteSize);
return true;
}
size_t SkMemberInfo::getSize(const SkDisplayable* displayable) const {
size_t byteSize;
switch (fType) {
case SkType_MemberProperty:
byteSize = GetSize(propertyType());
break;
case SkType_Array: {
SkDisplayTypes type;
if (displayable == NULL)
return sizeof(int);
if (displayable->getType() == SkType_Array) {
SkDisplayArray* dispArray = (SkDisplayArray*) displayable;
type = dispArray->values.getType();
} else
type = propertyType();
SkTDOperandArray* array = (SkTDOperandArray*) memberData(displayable);
byteSize = GetSize(type) * array->count();
} break;
default:
byteSize = GetSize((SkDisplayTypes) fType);
}
return byteSize;
}
void SkMemberInfo::getString(const SkDisplayable* displayable, SkString** string) const {
if (fType == SkType_MemberProperty) {
SkScriptValue value;
displayable->getProperty(propertyIndex(), &value);
SkASSERT(value.fType == SkType_String);
*string = value.fOperand.fString;
return;
}
SkASSERT(fCount == sizeof(SkString) / sizeof(SkScalar));
SkASSERT(fType == SkType_String || fType == SkType_DynamicString);
void* valuePtr = memberData(displayable);
*string = (SkString*) valuePtr;
}
void SkMemberInfo::getValue(const SkDisplayable* displayable, SkOperand value[], int count) const {
SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
SkASSERT(count == fCount);
void* valuePtr = memberData(displayable);
size_t byteSize = getSize(displayable);
SkASSERT(sizeof(value[0].fScalar) == sizeof(value[0])); // no support for 64 bit pointers, yet
memcpy(value, valuePtr, byteSize);
}
void SkMemberInfo::setString(SkDisplayable* displayable, SkString* value) const {
SkString* string = (SkString*) memberData(displayable);
string->set(*value);
displayable->dirty();
}
void SkMemberInfo::setValue(SkDisplayable* displayable, const SkOperand values[],
int count) const {
SkASSERT(sizeof(values[0].fScalar) == sizeof(values[0])); // no support for 64 bit pointers, yet
char* dst = (char*) memberData(displayable);
if (fType == SkType_Array) {
SkTDScalarArray* array = (SkTDScalarArray* ) dst;
array->setCount(count);
dst = (char*) array->begin();
}
memcpy(dst, values, count * sizeof(SkOperand));
displayable->dirty();
}
static inline bool is_between(int c, int min, int max)
{
return (unsigned)(c - min) <= (unsigned)(max - min);
}
static inline bool is_hex(int c)
{
if (is_between(c, '0', '9'))
return true;
c |= 0x20; // make us lower-case
if (is_between(c, 'a', 'f'))
return true;
return false;
}
bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage,
int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
const char rawValue[], size_t rawValueLen) const
{
SkString valueStr(rawValue, rawValueLen);
SkScriptValue scriptValue;
scriptValue.fType = SkType_Unknown;
scriptValue.fOperand.fS32 = 0;
SkDisplayTypes type = getType();
SkAnimatorScript engine(maker, displayable, type);
if (arrayStorage)
displayable = NULL;
bool success = true;
void* untypedStorage = NULL;
if (displayable && fType != SkType_MemberProperty && fType != SkType_MemberFunction)
untypedStorage = (SkTDOperandArray*) memberData(displayable);
if (type == SkType_ARGB) {
// for both SpiderMonkey and SkiaScript, substitute any #xyz or #xxyyzz first
// it's enough to expand the colors into 0xFFxxyyzz
const char* poundPos;
while ((poundPos = strchr(valueStr.c_str(), '#')) != NULL) {
size_t offset = poundPos - valueStr.c_str();
if (valueStr.size() - offset < 4)
break;
char r = poundPos[1];
char g = poundPos[2];
char b = poundPos[3];
if (is_hex(r) == false || is_hex(g) == false || is_hex(b) == false)
break;
char hex = poundPos[4];
if (is_hex(hex) == false) {
valueStr.insertUnichar(offset + 1, r);
valueStr.insertUnichar(offset + 3, g);
valueStr.insertUnichar(offset + 5, b);
}
*(char*) poundPos = '0'; // overwrite '#'
valueStr.insert(offset + 1, "xFF");
}
}
if (SkDisplayType::IsDisplayable(&maker, type) || SkDisplayType::IsEnum(&maker, type) || type == SkType_ARGB)
goto scriptCommon;
switch (type) {
case SkType_String:
#if 0
if (displayable && displayable->isAnimate()) {
goto noScriptString;
}
if (strncmp(rawValue, "#string:", sizeof("#string:") - 1) == 0) {
SkASSERT(sizeof("string") == sizeof("script"));
char* stringHeader = valueStr.writable_str();
memcpy(&stringHeader[1], "script", sizeof("script") - 1);
rawValue = valueStr.c_str();
goto noScriptString;
} else
#endif
if (strncmp(rawValue, "#script:", sizeof("#script:") - 1) != 0)
goto noScriptString;
valueStr.remove(0, 8);
case SkType_Unknown:
case SkType_Int:
case SkType_MSec: // for the purposes of script, MSec is treated as a Scalar
case SkType_Point:
case SkType_3D_Point:
case SkType_Float:
case SkType_Array:
scriptCommon: {
const char* script = valueStr.c_str();
success = engine.evaluateScript(&script, &scriptValue);
if (success == false) {
maker.setScriptError(engine);
return false;
}
}
SkASSERT(success);
if (scriptValue.fType == SkType_Displayable) {
if (type == SkType_String) {
const char* charPtr;
maker.findKey(scriptValue.fOperand.fDisplayable, &charPtr);
scriptValue.fOperand.fString = new SkString(charPtr);
scriptValue.fType = SkType_String;
engine.SkScriptEngine::track(scriptValue.fOperand.fString);
break;
}
SkASSERT(SkDisplayType::IsDisplayable(&maker, type));
if (displayable)
displayable->setReference(this, scriptValue.fOperand.fDisplayable);
else
arrayStorage->begin()[0].fDisplayable = scriptValue.fOperand.fDisplayable;
return true;
}
if (type != scriptValue.fType) {
if (scriptValue.fType == SkType_Array) {
engine.forget(scriptValue.getArray());
goto writeStruct; // real structs have already been written by script
}
switch (type) {
case SkType_String:
success = engine.convertTo(SkType_String, &scriptValue);
break;
case SkType_MSec:
case SkType_Float:
success = engine.convertTo(SkType_Float, &scriptValue);
break;
case SkType_Int:
success = engine.convertTo(SkType_Int, &scriptValue);
break;
case SkType_Array:
success = engine.convertTo(arrayType(), &scriptValue);
// !!! incomplete; create array of appropriate type and add scriptValue to it
SkASSERT(0);
break;
case SkType_Displayable:
case SkType_Drawable:
return false; // no way to convert other types to this
default: // to avoid warnings
break;
}
if (success == false)
return false;
}
if (type == SkType_MSec)
scriptValue.fOperand.fMSec = SkScalarMulRound(scriptValue.fOperand.fScalar, 1000);
scriptValue.fType = type;
break;
noScriptString:
case SkType_DynamicString:
if (fType == SkType_MemberProperty && displayable) {
SkString string(rawValue, rawValueLen);
SkScriptValue scriptValue;
scriptValue.fOperand.fString = &string;
scriptValue.fType = SkType_String;
displayable->setProperty(propertyIndex(), scriptValue);
} else if (displayable) {
SkString* string = (SkString*) memberData(displayable);
string->set(rawValue, rawValueLen);
} else {
SkASSERT(arrayStorage->count() == 1);
arrayStorage->begin()->fString->set(rawValue, rawValueLen);
}
goto dirty;
case SkType_Base64: {
SkBase64 base64;
base64.decode(rawValue, rawValueLen);
*(SkBase64* ) untypedStorage = base64;
} goto dirty;
default:
SkASSERT(0);
break;
}
// if (SkDisplayType::IsStruct(type) == false)
{
writeStruct:
if (writeValue(displayable, arrayStorage, storageOffset, maxStorage,
untypedStorage, outType, scriptValue)) {
maker.setErrorCode(SkDisplayXMLParserError::kUnexpectedType);
return false;
}
}
dirty:
if (displayable)
displayable->dirty();
return true;
}
bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage,
int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
SkString& raw) const {
return setValue(maker, arrayStorage, storageOffset, maxStorage, displayable, outType, raw.c_str(),
raw.size());
}
bool SkMemberInfo::writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage,
int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType,
SkScriptValue& scriptValue) const
{
SkOperand* storage = untypedStorage ? (SkOperand*) untypedStorage : arrayStorage ?
arrayStorage->begin() : NULL;
if (storage)
storage += storageOffset;
SkDisplayTypes type = getType();
if (fType == SkType_MemberProperty) {
if(displayable)
displayable->setProperty(propertyIndex(), scriptValue);
else {
SkASSERT(storageOffset < arrayStorage->count());
switch (scriptValue.fType) {
case SkType_Boolean:
case SkType_Float:
case SkType_Int:
memcpy(&storage->fScalar, &scriptValue.fOperand.fScalar, sizeof(SkScalar));
break;
case SkType_Array:
memcpy(&storage->fScalar, scriptValue.fOperand.fArray->begin(), scriptValue.fOperand.fArray->count() * sizeof(SkScalar));
break;
case SkType_String:
storage->fString->set(*scriptValue.fOperand.fString);
break;
default:
SkASSERT(0); // type isn't handled yet
}
}
} else if (fType == SkType_MemberFunction) {
SkASSERT(scriptValue.fType == SkType_Array);
if (displayable)
displayable->executeFunction(displayable, this, scriptValue.fOperand.fArray, NULL);
else {
int count = scriptValue.fOperand.fArray->count();
// SkASSERT(maxStorage == 0 || count == maxStorage);
if (arrayStorage->count() == 2)
arrayStorage->setCount(2 * count);
else {
storageOffset *= count;
SkASSERT(count + storageOffset <= arrayStorage->count());
}
memcpy(&(*arrayStorage)[storageOffset], scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
}
} else if (fType == SkType_Array) {
SkTypedArray* destArray = (SkTypedArray*) (untypedStorage ? untypedStorage : arrayStorage);
SkASSERT(destArray);
// destArray->setCount(0);
if (scriptValue.fType != SkType_Array) {
SkASSERT(type == scriptValue.fType);
// SkASSERT(storageOffset + 1 <= maxStorage);
destArray->setCount(storageOffset + 1);
(*destArray)[storageOffset] = scriptValue.fOperand;
} else {
if (type == SkType_Unknown) {
type = scriptValue.fOperand.fArray->getType();
destArray->setType(type);
}
SkASSERT(type == scriptValue.fOperand.fArray->getType());
int count = scriptValue.fOperand.fArray->count();
// SkASSERT(storageOffset + count <= maxStorage);
destArray->setCount(storageOffset + count);
memcpy(destArray->begin() + storageOffset, scriptValue.fOperand.fArray->begin(), sizeof(SkOperand) * count);
}
} else if (type == SkType_String) {
SkString* string = untypedStorage ? (SkString*) untypedStorage : (*arrayStorage)[storageOffset].fString;
string->set(*scriptValue.fOperand.fString);
} else if (type == SkType_ARGB && outType == SkType_Float) {
SkTypedArray* array = scriptValue.fOperand.fArray;
SkASSERT(scriptValue.fType == SkType_Int || scriptValue.fType == SkType_ARGB ||
scriptValue.fType == SkType_Array);
SkASSERT(scriptValue.fType != SkType_Array || (array != NULL &&
array->getType() == SkType_Int));
int numberOfColors = scriptValue.fType == SkType_Array ? array->count() : 1;
int numberOfComponents = numberOfColors * 4;
// SkASSERT(maxStorage == 0 || maxStorage == numberOfComponents);
if (maxStorage == 0)
arrayStorage->setCount(numberOfComponents);
for (int index = 0; index < numberOfColors; index++) {
SkColor color = scriptValue.fType == SkType_Array ?
(SkColor) array->begin()[index].fS32 : (SkColor) scriptValue.fOperand.fS32;
storage[0].fScalar = SkIntToScalar(SkColorGetA(color));
storage[1].fScalar = SkIntToScalar(SkColorGetR(color));
storage[2].fScalar = SkIntToScalar(SkColorGetG(color));
storage[3].fScalar = SkIntToScalar(SkColorGetB(color));
storage += 4;
}
} else if (SkDisplayType::IsStruct(NULL /* !!! maker*/, type)) {
if (scriptValue.fType != SkType_Array)
return true; // error
SkASSERT(sizeof(SkScalar) == sizeof(SkOperand)); // !!! no 64 bit pointer support yet
int count = scriptValue.fOperand.fArray->count();
if (count > 0) {
SkASSERT(fCount == count);
memcpy(storage, scriptValue.fOperand.fArray->begin(), count * sizeof(SkOperand));
}
} else if (scriptValue.fType == SkType_Array) {
SkASSERT(scriptValue.fOperand.fArray->getType() == type);
SkASSERT(scriptValue.fOperand.fArray->count() == getCount());
memcpy(storage, scriptValue.fOperand.fArray->begin(), getCount() * sizeof(SkOperand));
} else {
memcpy(storage, &scriptValue.fOperand, sizeof(SkOperand));
}
return false;
}
//void SkMemberInfo::setValue(SkDisplayable* displayable, const char value[], const char name[]) const {
// void* valuePtr = (void*) ((char*) displayable + fOffset);
// switch (fType) {
// case SkType_Point3D: {
// static const char xyz[] = "x|y|z";
// int index = find_one(xyz, name);
// SkASSERT(index >= 0);
// valuePtr = (void*) ((char*) valuePtr + index * sizeof(SkScalar));
// } break;
// default:
// SkASSERT(0);
// }
// SkParse::FindScalar(value, (SkScalar*) valuePtr);
// displayable->dirty();
//}
#if SK_USE_CONDENSED_INFO == 0
// Find Nth memberInfo
const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, int* index) {
SkASSERT(*index >= 0);
if (info->fType == SkType_BaseClassInfo) {
const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, index);
if (result != NULL)
return result;
if (--count == 0)
return NULL;
info++;
}
SkASSERT(info->fName);
SkASSERT(info->fType != SkType_BaseClassInfo);
if (*index >= count) {
*index -= count;
return NULL;
}
return &info[*index];
}
// Find named memberinfo
const SkMemberInfo* SkMemberInfo::Find(const SkMemberInfo info[], int count, const char** matchPtr) {
const char* match = *matchPtr;
if (info->fType == SkType_BaseClassInfo) {
const SkMemberInfo* inherited = (SkMemberInfo*) info->fName;
const SkMemberInfo* result = SkMemberInfo::Find(inherited, info->fCount, matchPtr);
if (result != NULL)
return result;
if (--count == 0)
return NULL;
info++;
}
SkASSERT(info->fName);
SkASSERT(info->fType != SkType_BaseClassInfo);
int index = SkStrSearch(&info->fName, count, match, sizeof(*info));
if (index < 0 || index >= count)
return NULL;
return &info[index];
}
const SkMemberInfo* SkMemberInfo::getInherited() const {
return (SkMemberInfo*) fName;
}
#endif // SK_USE_CONDENSED_INFO == 0
#if 0
bool SkMemberInfo::SetValue(void* valuePtr, const char value[], SkDisplayTypes type,
int count) {
switch (type) {
case SkType_Animate:
case SkType_BaseBitmap:
case SkType_Bitmap:
case SkType_Dash:
case SkType_Displayable:
case SkType_Drawable:
case SkType_Matrix:
case SkType_Path:
case SkType_Text:
case SkType_3D_Patch:
return false; // ref to object; caller must resolve
case SkType_MSec: {
SkParse::FindMSec(value, (SkMSec*) valuePtr);
} break;
case SkType_3D_Point:
case SkType_Point:
// case SkType_PointArray:
case SkType_ScalarArray:
SkParse::FindScalars(value, (SkScalar*) valuePtr, count);
break;
default:
SkASSERT(0);
}
return true;
}
#endif