aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar halcanary <halcanary@google.com>2016-01-20 10:00:26 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2016-01-20 10:00:26 -0800
commit76097f823598076a440ed9dc7fa6611583308002 (patch)
tree8950634c573153434b05e947021178b063fe8c9c
parent5352015fa318fc6c25b789cb0a81d2d586d6121f (diff)
SkValue: implementation, unit test
-rw-r--r--gyp/core.gypi1
-rw-r--r--src/core/SkValue.cpp217
-rw-r--r--src/core/SkValue.h77
-rw-r--r--tests/ValueTest.cpp104
4 files changed, 377 insertions, 22 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi
index 1ecaa3ed22..ff1282f4b4 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -289,6 +289,7 @@
'<(skia_src_path)/core/SkValidatingReadBuffer.cpp',
'<(skia_src_path)/core/SkValidatingReadBuffer.h',
'<(skia_src_path)/core/SkValidationUtils.h',
+ '<(skia_src_path)/core/SkValue.cpp',
'<(skia_src_path)/core/SkVarAlloc.cpp',
'<(skia_src_path)/core/SkVertState.cpp',
'<(skia_src_path)/core/SkWriteBuffer.cpp',
diff --git a/src/core/SkValue.cpp b/src/core/SkValue.cpp
new file mode 100644
index 0000000000..8f58dd6ef9
--- /dev/null
+++ b/src/core/SkValue.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <unordered_map>
+#include <vector>
+
+#include "SkData.h"
+#include "SkMatrix.h"
+#include "SkValue.h"
+
+class SkValue::Obj {
+public:
+ void set(SkValue::Key k, SkValue&& v) { fMap[k] = std::move(v); }
+ const SkValue* get(SkValue::Key k) const {
+ auto it = fMap.find(k);
+ return it != fMap.end() ? &it->second : nullptr;
+ }
+ void foreach(std::function<void(Key, const SkValue&)> fn) const {
+ for (const auto& pair : fMap) {
+ fn(pair.first, pair.second);
+ }
+ }
+
+private:
+ std::unordered_map<SkValue::Key, SkValue> fMap;
+};
+
+class SkValue::Arr {
+public:
+ size_t length() const { return fVec.size(); }
+ void append(SkValue&& val) { fVec.emplace_back(std::move(val)); }
+ const SkValue& at(size_t index) const {
+ SkASSERT(index < fVec.size());
+ return fVec[index];
+ }
+
+private:
+ std::vector<SkValue> fVec;
+};
+
+SkValue::SkValue() : fType(Null) {}
+
+SkValue::SkValue(Type type) : fType(type) {}
+
+SkValue::SkValue(const SkValue& o) {
+ memcpy(this, &o, sizeof(o));
+ if (this->isData()) {
+ fBytes->ref();
+ } else if (this->isObject()) {
+ fObject = new Obj(*fObject);
+ } else if (Array == fType) {
+ fArray = new Arr(*fArray);
+ }
+}
+
+SkValue::SkValue(SkValue&& o) {
+ memcpy(this, &o, sizeof(o));
+ new (&o) SkValue();
+}
+
+SkValue& SkValue::operator=(const SkValue& o) {
+ if (this != &o) {
+ this->~SkValue();
+ new (this) SkValue(o);
+ }
+ return *this;
+}
+
+SkValue& SkValue::operator=(SkValue&& o) {
+ if (this != &o) {
+ this->~SkValue();
+ new (this) SkValue(std::move(o));
+ }
+ return *this;
+}
+
+SkValue::~SkValue() {
+ if (this->isData()) {
+ fBytes->unref();
+ } else if (this->isObject()) {
+ delete fObject;
+ } else if (Array == fType) {
+ delete fArray;
+ }
+}
+
+template <typename T>
+SkValue SkValue::FromT(SkValue::Type type, T SkValue::*mp, T t) {
+ SkValue v(type);
+ v.*mp = t;
+ return v;
+}
+
+SkValue SkValue::FromS32(int32_t x) { return FromT(S32, &SkValue::fS32, x); }
+SkValue SkValue::FromU32(uint32_t x) { return FromT(U32, &SkValue::fU32, x); }
+SkValue SkValue::FromF32(float x) { return FromT(F32, &SkValue::fF32, x); }
+
+int32_t SkValue::s32() const { SkASSERT(S32 == fType); return fS32; }
+uint32_t SkValue::u32() const { SkASSERT(U32 == fType); return fU32; }
+float SkValue::f32() const { SkASSERT(F32 == fType); return fF32; }
+
+SkValue SkValue::FromBytes(SkData* data) {
+ if (!data) {
+ return SkValue();
+ }
+ SkValue v(Bytes);
+ v.fBytes = SkRef(data);
+ return v;
+}
+
+SkValue SkValue::Object(SkValue::Type t) {
+ SkValue v(t);
+ SkASSERT(v.isObject());
+ v.fObject = new Obj;
+ return v;
+}
+
+SkValue SkValue::ValueArray() {
+ SkValue v(Array);
+ v.fArray = new Arr;
+ return v;
+}
+
+SkData* SkValue::bytes() const {
+ SkASSERT(this->isData());
+ return fBytes;
+}
+
+void SkValue::set(SkValue::Key k, SkValue v) {
+ SkASSERT(this->isObject());
+ fObject->set(k, std::move(v));
+}
+
+void SkValue::foreach(std::function<void(Key, const SkValue&)> fn) const {
+ SkASSERT(this->isObject());
+ fObject->foreach(fn);
+}
+
+size_t SkValue::length() const {
+ SkASSERT(Array == fType);
+ return fArray->length();
+}
+
+const SkValue& SkValue::at(size_t index) const {
+ SkASSERT(Array == fType);
+ return fArray->at(index);
+}
+
+void SkValue::append(SkValue val) {
+ SkASSERT(Array == fType);
+ fArray->append(std::move(val));
+}
+
+template <typename T>
+const T* SkValue::asTs(SkValue::Type t, int* count) const {
+ SkASSERT(t == fType && this->isData());
+ SkASSERT(count);
+ *count = fBytes->size() / sizeof(T);
+ return static_cast<const T*>(fBytes->data());
+}
+
+const uint16_t* SkValue::u16s(int* c) const { return this->asTs<uint16_t>(U16s, c); }
+const uint32_t* SkValue::u32s(int* c) const { return this->asTs<uint32_t>(U32s, c); }
+const float* SkValue::f32s(int* c) const { return this->asTs<float >(F32s, c); }
+
+template <typename T>
+SkValue SkValue::FromTs(SkValue::Type type, SkData* data) {
+ SkValue val(type);
+ val.fBytes = SkRef(data);
+ SkASSERT(val.isData());
+ SkASSERT(0 == (reinterpret_cast<uintptr_t>(data->bytes()) & (sizeof(T)-1)));
+ return val;
+}
+
+SkValue SkValue::FromU16s(SkData* d) { return FromTs<uint16_t>(U16s, d); }
+SkValue SkValue::FromU32s(SkData* d) { return FromTs<uint32_t>(U32s, d); }
+SkValue SkValue::FromF32s(SkData* d) { return FromTs< float>(F32s, d); }
+
+////////////////////////////////////////////////////////////////////////////////
+
+template<> SkValue SkToValue<SkMatrix>(const SkMatrix& mat) {
+ auto val = SkValue::Object(SkValue::Matrix);
+ for (int i = 0; i < 9; ++i) {
+ if (mat[i] != SkMatrix::I()[i]) {
+ val.set(i, SkValue::FromF32(mat[i]));
+ }
+ }
+ return val;
+}
+
+template<> bool SkFromValue<SkMatrix>(const SkValue& val, SkMatrix* m){
+ SkASSERT(val.type() == SkValue::Matrix);
+ if (val.type() != SkValue::Matrix) {
+ return false;
+ }
+ *m = SkMatrix::I();
+ bool good = true;
+ auto fn = [&](SkValue::Key key, const SkValue& v) {
+ if (key < 9) {
+ if (v.type() != SkValue::F32) {
+ SkASSERT(false);
+ good = false;
+ } else {
+ (*m)[key] = v.f32();
+ }
+ } else {
+ SkASSERT(false);
+ good = false;
+ }
+ };
+ val.foreach(fn);
+ return good;
+}
diff --git a/src/core/SkValue.h b/src/core/SkValue.h
index dd42b3a889..5ff9eadc25 100644
--- a/src/core/SkValue.h
+++ b/src/core/SkValue.h
@@ -11,6 +11,8 @@
#include "SkTypes.h"
#include <functional>
+class SkData;
+
class SkValue {
public:
enum Type : uint32_t {
@@ -20,19 +22,22 @@ public:
Bytes, S16s, U16s, S32s, U32s, S64s, U64s, F32s, F64s,
Array,
+ kMaxBuiltin = 0xFF,
// 256-2147483647 may be used by Skia for public Object types.
+ Matrix,
- // 2147483648+ won't be used by Skia. They're open for client-specific use, testing, etc.
+ kMaxPublicObject = 0x7FFFFFFF,
+ // 2147483648+ won't be used by Skia. They're open for
+ // client-specific use, testing, etc.
};
- enum Key : uint32_t {
- // Each Object type may define its own namespace of Key values,
- // so there are no pre-defined Keys here.
- //
- // This is just a reminder that they must fit in a uint32_t,
- // and that their namespace is distinct from other uint32_ts (e.g. Type).
- };
+ // Each Object type may define its own namespace of Key values,
+ // so there are no pre-defined Keys here.
+ //
+ // This is just a reminder that they must fit in a uint32_t,
+ // and that their namespace is distinct from other uint32_ts (e.g. Type).
+ typedef uint32_t Key;
SkValue();
SkValue(const SkValue&);
@@ -46,36 +51,66 @@ public:
static SkValue FromS32(int32_t);
static SkValue FromU32(uint32_t);
static SkValue FromF32(float);
- static SkValue FromBytes(const void*, size_t); // Copies.
+ static SkValue FromBytes(SkData*);
+ static SkValue FromU16s(SkData*);
+ static SkValue FromU32s(SkData*);
+ static SkValue FromF32s(SkData*);
+ static SkValue ValueArray();
static SkValue Object(Type);
- Type type() const;
+ Type type() const { return fType; }
// These remaining methods may assert they're called on a value of the appropriate type.
int32_t s32() const;
uint32_t u32() const;
float f32() const;
+ SkData* bytes() const;
- const void* bytes() const;
- size_t count() const;
+ const uint16_t* u16s(int* count) const;
+ const uint32_t* u32s(int* count) const;
+ const float* f32s(int* count) const;
+ // Object
void set(Key, SkValue);
- const SkValue* get(Key) const;
void foreach(std::function<void(Key, const SkValue&)>) const;
+ // Array
+ size_t length() const;
+ const SkValue& at(size_t) const;
+ void append(SkValue);
+
private:
- class Bytes;
- class Object;
+ class Obj;
+ class Arr;
Type fType;
union {
- int32_t fS32;
- uint32_t fU32;
- float fF32;
- class Bytes* fBytes;
- class Object* fObject;
+ int32_t fS32;
+ uint32_t fU32;
+ float fF32;
+ SkData* fBytes;
+ Obj* fObject;
+ Arr* fArray;
};
+
+ SkValue(Type);
+ bool isObject() const { return fType > kMaxBuiltin; }
+ bool isData() const {
+ return Bytes == fType
+ || U16s == fType
+ || U32s == fType
+ || F32s == fType;
+ }
+ template <typename T> static SkValue FromT(SkValue::Type, T SkValue::*, T);
+ template <typename T> static SkValue FromTs(SkValue::Type, SkData*);
+ template <typename T> const T* asTs(SkValue::Type, int*) const;
};
-#endif//SkValue_DEFINED
+template <typename T>
+SkValue SkToValue(const T&);
+
+template <typename T>
+bool SkFromValue(const SkValue&, T*);
+
+#endif // SkValue_DEFINED
diff --git a/tests/ValueTest.cpp b/tests/ValueTest.cpp
index 220ef2f9ae..e046db7f9e 100644
--- a/tests/ValueTest.cpp
+++ b/tests/ValueTest.cpp
@@ -5,5 +5,107 @@
* found in the LICENSE file.
*/
-#include "Test.h"
+#include "SkData.h"
+#include "SkMatrix.h"
#include "SkValue.h"
+#include "Test.h"
+
+static const SkValue::Type example_type =
+ SkValue::Type(SkValue::kMaxPublicObject + 1);
+
+enum { kExampleS32, kExampleF32, kExampleU32, kExampleObject, kExampleBytes,
+ kExampleF32s, kExampleU32s, kExampleU16s, kExampleArray };
+
+static const uint16_t aU16[] = { 1, 2, 3, 4 };
+static const uint32_t aU32[] = { 5, 6, 7, 8 };
+static const float aF32[] = { 9.0f, 9.125f, 9.25f };
+static const char hello[] = "HELLO";
+
+static SkValue make_example(skiatest::Reporter* r, int level = 4) {
+ auto value = SkValue::Object(example_type);
+ value.set(kExampleU32, SkValue::FromU32(1000));
+ value.set(kExampleS32, SkValue::FromS32(-123));
+ value.set(kExampleF32, SkValue::FromF32(0.5f));
+ value.set(kExampleU32, SkValue::FromU32(1234));
+ if (level > 0) {
+ value.set(kExampleObject, make_example(r, 0));
+ value.set(kExampleObject, make_example(r, level - 1)); // replace
+ }
+ SkAutoTUnref<SkData> data(SkData::NewWithCString(hello));
+ value.set(kExampleBytes, SkValue::FromBytes(data));
+
+ SkAutoTUnref<SkData> dataU16(SkData::NewWithCopy(aU16, sizeof(aU16)));
+ SkAutoTUnref<SkData> dataU32(SkData::NewWithCopy(aU32, sizeof(aU32)));
+ SkAutoTUnref<SkData> dataF32(SkData::NewWithCopy(aF32, sizeof(aF32)));
+ value.set(kExampleU16s, SkValue::FromU16s(dataU16));
+ value.set(kExampleU32s, SkValue::FromU32s(dataU32));
+ value.set(kExampleF32s, SkValue::FromF32s(dataF32));
+
+ auto varray = SkValue::ValueArray();
+ varray.append(SkValue::FromU32(99));
+ varray.append(SkValue::FromS32(-99));
+ value.set(kExampleArray, std::move(varray));
+ return value;
+}
+
+DEF_TEST(Value, r) {
+ SkValue val = make_example(r);
+ REPORTER_ASSERT(r, example_type == val.type());
+ SkValue valCopy = val;
+ REPORTER_ASSERT(r, example_type == valCopy.type());
+ valCopy.set(4321, SkValue());
+ auto fn = [&](SkValue::Key k, const SkValue& v){
+ int count;
+ switch (k) {
+ case kExampleS32:
+ REPORTER_ASSERT(r, -123 == v.s32());
+ break;
+ case kExampleF32:
+ REPORTER_ASSERT(r, 0.5f == v.f32());
+ break;
+ case kExampleU32:
+ REPORTER_ASSERT(r, 1234 == v.u32());
+ break;
+ case kExampleObject:
+ REPORTER_ASSERT(r, example_type == v.type());
+ break;
+ case kExampleBytes:
+ REPORTER_ASSERT(r, v.type() == SkValue::Bytes && v.bytes()
+ && v.bytes()->size() == sizeof(hello));
+ break;
+ case kExampleF32s:
+ REPORTER_ASSERT(r, v.type() == SkValue::F32s && v.bytes()
+ && v.bytes()->size() == sizeof(aF32)
+ && v.f32s(&count)
+ && count == SK_ARRAY_COUNT(aF32));
+ break;
+ case kExampleU32s:
+ REPORTER_ASSERT(r, v.type() == SkValue::U32s && v.bytes()
+ && v.bytes()->size() == sizeof(aU32)
+ && v.u32s(&count)
+ && count == SK_ARRAY_COUNT(aU32));
+ break;
+ case kExampleU16s:
+ REPORTER_ASSERT(r, v.type() == SkValue::U16s && v.bytes()
+ && v.bytes()->size() == sizeof(aU16)
+ && v.u16s(&count)
+ && count == SK_ARRAY_COUNT(aU16));
+ break;
+ case kExampleArray:
+ REPORTER_ASSERT(r, v.type() == SkValue::Array
+ && v.length() == 2);
+ break;
+ default:
+ ERRORF(r, "unexpected key");
+ }
+ };
+ val.foreach(fn);
+}
+
+DEF_TEST(Value_Matrix, r) {
+ auto m = SkMatrix::MakeTrans(900.0f, 1000.0f);
+ auto val = SkToValue(m);
+ SkMatrix dst;
+ REPORTER_ASSERT(r, SkFromValue(val, &dst));
+ REPORTER_ASSERT(r, dst == m);
+}