Index: src/core/SkTTopoSort.h |
diff --git a/src/core/SkTTopoSort.h b/src/core/SkTTopoSort.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..35b85eeb8147a24744b12cf6c7a30d99ff3f65ed |
--- /dev/null |
+++ b/src/core/SkTTopoSort.h |
@@ -0,0 +1,112 @@ |
+/* |
+ * Copyright 2015 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#ifndef SkTTopoSort_DEFINED |
+#define SkTTopoSort_DEFINED |
+ |
+#include "SkTDArray.h" |
+ |
+#ifdef SK_DEBUG |
+template <typename T, typename Traits = T> |
+void SkTTopoSort_CheckAllUnmarked(const SkTDArray<T*>& graph) { |
+ for (int i = 0; i < graph.count(); ++i) { |
+ SkASSERT(!Traits::IsTempMarked(graph[i])); |
+ SkASSERT(!Traits::WasOutput(graph[i])); |
+ } |
+} |
+ |
+template <typename T, typename Traits = T> |
+void SkTTopoSort_CleanExit(const SkTDArray<T*>& graph) { |
+ for (int i = 0; i < graph.count(); ++i) { |
+ SkASSERT(!Traits::IsTempMarked(graph[i])); |
+ SkASSERT(Traits::WasOutput(graph[i])); |
+ } |
+} |
+#endif |
+ |
+// Recursively visit a node and all the other nodes it depends on. |
+// Return false if there is a loop. |
+template <typename T, typename Traits = T> |
+bool SkTTopoSort_Visit(T* node, SkTDArray<T*>* result) { |
+ if (Traits::IsTempMarked(node)) { |
+ // There is a loop. |
+ return false; |
+ } |
+ |
+ // If the node under consideration has been already been output it means it |
+ // (and all the nodes it depends on) are already in 'result'. |
+ if (!Traits::WasOutput(node)) { |
+ // This node hasn't been output yet. Recursively assess all the |
+ // nodes it depends on outputing them first. |
+ Traits::SetTempMark(node); |
+ for (int i = 0; i < Traits::NumDependencies(node); ++i) { |
+ if (!SkTTopoSort_Visit<T, Traits>(Traits::Dependency(node, i), result)) { |
+ return false; |
+ } |
+ } |
+ Traits::Output(node, result->count()); // mark this node as output |
+ Traits::ResetTempMark(node); |
+ |
+ *result->append() = node; |
+ } |
+ |
+ return true; |
+} |
+ |
+// Topologically sort the nodes in 'graph'. For this sort, when node 'i' depends |
+// on node 'j' it means node 'j' must appear in the result before node 'i'. |
+// A false return value means there was a loop and the contents of 'graph' will |
+// be in some arbitrary state. |
+// |
+// Traits requires: |
+// static void Output(T* t, int index) { ... } // 'index' is 't's position in the result |
+// static bool WasOutput(const T* t) { ... } |
+// |
+// static void SetTempMark(T* t) { ... } // transiently used during toposort |
+// static void ResetTempMark(T* t) { ... } |
+// static bool IsTempMarked(const T* t) { ... } |
+// |
+// static int NumDependencies(const T* t) { ... } // 't' will be output after all the other - |
+// static T* Dependency(T* t, int index) { ... } // nodes on which it depends |
+// We'll look on T for these by default, or you can pass a custom Traits type. |
+// |
+// TODO: potentially add a version that takes a seed node and just outputs that |
+// node and all the nodes on which it depends. This could be used to partially |
+// flush a drawTarget DAG. |
+template <typename T, typename Traits = T> |
+bool SkTTopoSort(SkTDArray<T*>* graph) { |
+ SkTDArray<T*> result; |
+ |
+#ifdef SK_DEBUG |
+ SkTTopoSort_CheckAllUnmarked<T, Traits>(*graph); |
+#endif |
+ |
+ result.setReserve(graph->count()); |
+ |
+ for (int i = 0; i < graph->count(); ++i) { |
+ if (Traits::WasOutput((*graph)[i])) { |
+ // This node was depended on by some earlier node and has already |
+ // been output |
+ continue; |
+ } |
+ |
+ // Output this node after all the nodes it depends on have been output. |
+ if (!SkTTopoSort_Visit<T, Traits>((*graph)[i], &result)) { |
+ return false; |
+ } |
+ } |
+ |
+ SkASSERT(graph->count() == result.count()); |
+ graph->swap(result); |
+ |
+#ifdef SK_DEBUG |
+ SkTTopoSort_CleanExit<T, Traits>(*graph); |
+#endif |
+ return true; |
+} |
+ |
+#endif |