summaryrefslogtreecommitdiff
path: root/Runtime/Math/Gradient.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Math/Gradient.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Math/Gradient.cpp')
-rw-r--r--Runtime/Math/Gradient.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/Runtime/Math/Gradient.cpp b/Runtime/Math/Gradient.cpp
new file mode 100644
index 0000000..9544a2b
--- /dev/null
+++ b/Runtime/Math/Gradient.cpp
@@ -0,0 +1,310 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ObjectDefines.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Math/Gradient.h"
+
+GradientNEW::GradientNEW()
+: m_NumColorKeys(2)
+, m_NumAlphaKeys(2)
+{
+ m_Keys[0] = m_Keys[1] = ColorRGBA32(0xffffffff);
+ m_ColorTime[0] = m_AlphaTime[0] = NormalizedToWord(0.0f);
+ m_ColorTime[1] = m_AlphaTime[1] = NormalizedToWord(1.0f);
+
+ for(UInt32 i = 2; i < kGradientMaxNumKeys; i++)
+ {
+ m_Keys[i] = ColorRGBA32(0);
+ m_ColorTime[i] = NormalizedToWord(0.0f);
+ m_AlphaTime[i] = NormalizedToWord(0.0f);
+ }
+}
+
+GradientNEW::~GradientNEW()
+{
+}
+
+void GradientNEW::SetKeys (ColorKey* colorKeys, unsigned numColorKeys, AlphaKey* alphaKeys, unsigned numAlphaKeys)
+{
+ SetColorKeys (colorKeys, numColorKeys);
+ SetAlphaKeys (alphaKeys, numAlphaKeys);
+}
+
+void GradientNEW::SwapColorKeys(int i, int j)
+{
+ ColorRGBA32 tmpCol = m_Keys[i];
+ UInt16 tmpTime = m_ColorTime[i];
+ m_Keys[i].r = m_Keys[j].r;
+ m_Keys[i].g = m_Keys[j].g;
+ m_Keys[i].b = m_Keys[j].b;
+ m_ColorTime[i] = m_ColorTime[j];
+ m_Keys[j].r = tmpCol.r;
+ m_Keys[j].g = tmpCol.g;
+ m_Keys[j].b = tmpCol.b;
+ m_ColorTime[j] = tmpTime;
+}
+
+void GradientNEW::SwapAlphaKeys(int i, int j)
+{
+ ColorRGBA32 tmpCol = m_Keys[i];
+ UInt16 tmpTime = m_AlphaTime[i];
+ m_Keys[i].a = m_Keys[j].a;
+ m_AlphaTime[i] = m_AlphaTime[j];
+ m_Keys[j].a = tmpCol.a;
+ m_AlphaTime[j] = tmpTime;
+}
+
+void GradientNEW::SetColorKeys (ColorKey* colorKeys, unsigned numKeys)
+{
+ DebugAssert (numKeys <= kGradientMaxNumKeys);
+ if (numKeys > kGradientMaxNumKeys)
+ numKeys = kGradientMaxNumKeys;
+
+ for (int i=0; i<numKeys; ++i)
+ {
+ const ColorRGBAf& color = colorKeys[i].m_Color;
+ m_Keys[i].r = NormalizedToByte(color.r);
+ m_Keys[i].g = NormalizedToByte(color.g);
+ m_Keys[i].b = NormalizedToByte(color.b);
+ m_ColorTime[i] = NormalizedToWord(colorKeys[i].m_Time);
+ }
+ m_NumColorKeys = numKeys;
+
+ // Ensure sorted!
+ int i = 0;
+ const int keyCount = m_NumColorKeys;
+ while ((i + 1) < keyCount)
+ {
+ if (m_ColorTime[i] > m_ColorTime[i+1])
+ {
+ SwapColorKeys(i, i + 1);
+ if (i > 0)
+ i -= 2;
+ }
+ i++;
+ }
+
+ ValidateColorKeys();
+}
+
+void GradientNEW::SetAlphaKeys (AlphaKey* alphaKeys, unsigned numKeys)
+{
+ DebugAssert (numKeys <= kGradientMaxNumKeys);
+ if (numKeys > kGradientMaxNumKeys)
+ numKeys = kGradientMaxNumKeys;
+
+ for (int i=0; i<numKeys; ++i)
+ {
+ float alpha = alphaKeys[i].m_Alpha;
+ m_Keys[i].a = NormalizedToByte(alpha);
+ m_AlphaTime[i] = NormalizedToWord(alphaKeys[i].m_Time);
+ }
+ m_NumAlphaKeys = numKeys;
+
+ // Ensure sorted!
+ int i = 0;
+ const int keyCount = m_NumAlphaKeys;
+ while ((i + 1) < keyCount)
+ {
+ if (m_AlphaTime[i] > m_AlphaTime[i+1])
+ {
+ SwapAlphaKeys(i, i + 1);
+ if (i > 0)
+ i -= 2;
+ }
+ i++;
+ }
+
+ ValidateAlphaKeys();
+}
+
+ColorRGBA32 GradientNEW::GetConstantColor () const
+{
+ return m_Keys[0];
+}
+
+void GradientNEW::SetConstantColor (ColorRGBA32 color)
+{
+ m_Keys[0] = color;
+ m_NumAlphaKeys = 1;
+ m_NumColorKeys = 1;
+}
+
+void GradientNEW::ValidateColorKeys()
+{
+ // Make sure there is a minimum of 2 keys
+ if(m_NumColorKeys < 2)
+ {
+ m_NumColorKeys = 2;
+ for(int rgb = 0; rgb < 3; rgb++)
+ m_Keys[1][rgb] = m_Keys[0][rgb];
+ m_ColorTime[0] = NormalizedToWord(0.0f);
+ m_ColorTime[1] = NormalizedToWord(1.0f);
+ }
+}
+
+void GradientNEW::ValidateAlphaKeys()
+{
+ // Make sure there is a minimum of 2 keys
+ if(m_NumAlphaKeys < 2)
+ {
+ m_NumAlphaKeys = 2;
+ m_Keys[1].a = m_Keys[0].a;
+ m_AlphaTime[0] = NormalizedToWord(0.0f);
+ m_AlphaTime[1] = NormalizedToWord(1.0f);
+ }
+}
+
+void GradientNEW::InitializeOptimized(OptimizedGradient& gradient)
+{
+ // Copy all time values
+ for(int i = 0; i < m_NumColorKeys; ++i)
+ gradient.times[i] = m_ColorTime[i];
+
+ for(int i = 0, i2 = m_NumColorKeys; i < m_NumAlphaKeys; ++i, ++i2)
+ gradient.times[i2] = m_AlphaTime[i];
+
+ // Remove duplicates
+ int keyCount = m_NumColorKeys + m_NumAlphaKeys;
+ for(int i = 0; i < keyCount-1; ++i)
+ {
+ for(int j = i+1; j < keyCount; )
+ {
+ if(gradient.times[i] == gradient.times[j])
+ {
+ std::swap(gradient.times[j], gradient.times[keyCount-1]);
+ keyCount--;
+ continue;
+ }
+ ++j;
+ }
+ }
+
+ // Sort
+ int i = 0;
+ while ((i + 1) < keyCount)
+ {
+ if (gradient.times[i] > gradient.times[i+1])
+ {
+ std::swap(gradient.times[i], gradient.times[i+1]);
+ if (i > 0)
+ i -= 2;
+ }
+ i++;
+ }
+
+ for(int i = 0; i < keyCount; ++i)
+ gradient.colors[i] = Evaluate(WordToNormalized(gradient.times[i]));
+ gradient.keyCount = keyCount;
+
+ for(int i = 1; i < keyCount; ++i)
+ gradient.rcp[i] = ((((1<<24)) / std::max<UInt32>(gradient.times[i] - gradient.times[i-1], 1)))+1;
+}
+
+template<class TransferFunction>
+void GradientNEW::Transfer (TransferFunction& transfer)
+{
+ AssertIf (kGradientMaxNumKeys > 9);
+
+ const char* kKeyNames [kGradientMaxNumKeys] = { "key0", "key1", "key2", "key3", "key4", "key5", "key6", "key7", };
+ for(UInt32 i = 0; i < kGradientMaxNumKeys; i++)
+ transfer.Transfer(m_Keys[i], kKeyNames[i], kHideInEditorMask);
+
+ const char* kColorTimeNames [kGradientMaxNumKeys] = { "ctime0", "ctime1", "ctime2", "ctime3", "ctime4", "ctime5", "ctime6", "ctime7", };
+ for(UInt32 i = 0; i < kGradientMaxNumKeys; i++)
+ transfer.Transfer(m_ColorTime[i], kColorTimeNames[i], kHideInEditorMask);
+
+ const char* kAlphaTimeNames [kGradientMaxNumKeys] = { "atime0", "atime1", "atime2", "atime3", "atime4", "atime5", "atime6", "atime7", };
+ for(UInt32 i = 0; i < kGradientMaxNumKeys; i++)
+ transfer.Transfer(m_AlphaTime[i], kAlphaTimeNames[i], kHideInEditorMask);
+
+ transfer.Transfer (m_NumColorKeys, "m_NumColorKeys", kHideInEditorMask);
+ transfer.Transfer (m_NumAlphaKeys, "m_NumAlphaKeys", kHideInEditorMask);
+ transfer.Align();
+
+ if(transfer.IsReading())
+ {
+ ValidateColorKeys();
+ ValidateAlphaKeys();
+ }
+}
+INSTANTIATE_TEMPLATE_TRANSFER(GradientNEW)
+
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+bool CompareColors(ColorRGBA32 c0, ColorRGBA32 c1, int tolerance)
+{
+ if(Abs(c0.r-c1.r) > tolerance)
+ return false;
+ if(Abs(c0.g-c1.g) > tolerance)
+ return false;
+ if(Abs(c0.b-c1.b) > tolerance)
+ return false;
+ if(Abs(c0.a-c1.a) > tolerance)
+ return false;
+ return true;
+}
+
+SUITE (GradientTests)
+{
+TEST (GradientTests_GradientEvaluate)
+{
+ // Set up rainbow gradient
+ GradientNEW gradient;
+ gradient.SetNumColorKeys(5);
+ gradient.SetNumAlphaKeys(3);
+ gradient.GetKey(0) = ColorRGBA32(0xff, 0x00, 0x00, 0xff);
+ gradient.GetKey(1) = ColorRGBA32(0xf8, 0xff, 0x00, 0x00);
+ gradient.GetKey(2) = ColorRGBA32(0x00, 0xff, 0x49, 0xff);
+ gradient.GetKey(3) = ColorRGBA32(0x22, 0x00, 0xff, 0x00);
+ gradient.GetKey(4) = ColorRGBA32(0xff, 0x00, 0xe6, 0x00);
+ gradient.GetColorTime(0) = 0x0000;
+ gradient.GetColorTime(1) = 0x40c1;
+ gradient.GetColorTime(2) = 0x9212;
+ gradient.GetColorTime(3) = 0xce4e;
+ gradient.GetColorTime(4) = 0xffff;
+ gradient.GetAlphaTime(0) = 0x0000;
+ gradient.GetAlphaTime(1) = 0x8000;
+ gradient.GetAlphaTime(2) = 0xffff;
+
+ CHECK_EQUAL(ColorRGBA32(0xff, 0x00, 0x00, 0xff) == gradient.Evaluate(0.0f), true);
+ CHECK_EQUAL(ColorRGBA32(0xfd, 0x31, 0x00, 0xe6) == gradient.Evaluate(0.05f), true);
+ CHECK_EQUAL(ColorRGBA32(0xfa, 0x96, 0x00, 0xb3) == gradient.Evaluate(0.15f), true);
+ CHECK_EQUAL(ColorRGBA32(0xf8, 0xfc, 0x00, 0x7f) == gradient.Evaluate(0.25f), true);
+ CHECK_EQUAL(ColorRGBA32(0xac, 0xff, 0x16, 0x4c) == gradient.Evaluate(0.35f), true);
+ CHECK_EQUAL(ColorRGBA32(0x5e, 0xff, 0x2d, 0x19) == gradient.Evaluate(0.45f), true);
+ CHECK_EQUAL(ColorRGBA32(0x10, 0xff, 0x44, 0x18) == gradient.Evaluate(0.55f), true);
+ CHECK_EQUAL(ColorRGBA32(0x0b, 0xa9, 0x86, 0x4b) == gradient.Evaluate(0.65f), true);
+ CHECK_EQUAL(ColorRGBA32(0x19, 0x3c, 0xd3, 0x7e) == gradient.Evaluate(0.75f), true);
+ CHECK_EQUAL(ColorRGBA32(0x54, 0x00, 0xf9, 0xb2) == gradient.Evaluate(0.85f), true);
+ CHECK_EQUAL(ColorRGBA32(0xc6, 0x00, 0xec, 0xe5) == gradient.Evaluate(0.95f), true);
+ CHECK_EQUAL(ColorRGBA32(0xff, 0x00, 0xe6, 0xff) == gradient.Evaluate(1.0f), true);
+
+ OptimizedGradient optGradient;
+ gradient.InitializeOptimized(optGradient);
+
+#if UNITY_LINUX
+#warning Investigate/fix GradientEvaluateTest!
+#else
+ // Being off by 1LSB is okay... (due to rounding)
+ for(float time = 0.0f; time <= 1.0f; time += 0.02f)
+ CHECK_EQUAL(CompareColors(optGradient.Evaluate(time), gradient.Evaluate(time), 1), true);
+
+ // ... but require exactness precicely at key times
+ for(int i = 0; i < 5; i++)
+ {
+ float time = WordToNormalized(gradient.GetColorTime(i));
+ CHECK_EQUAL(CompareColors(optGradient.Evaluate(time), gradient.Evaluate(time), 0), true);
+ }
+ for(int i = 0; i < 3; i++)
+ {
+ float time = WordToNormalized(gradient.GetAlphaTime(i));
+ CHECK_EQUAL(CompareColors(optGradient.Evaluate(time), gradient.Evaluate(time), 0), true);
+ }
+#endif
+}
+}
+
+#endif