1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Image/BsColorGradient.h"
4#include "Private/RTTI/BsColorGradientRTTI.h"
5#include "Debug/BsDebug.h"
6#include "Utility/BsBitwise.h"
7
8namespace bs
9{
10 ColorGradient::ColorGradient(const Color& color)
11 {
12 setConstant(color);
13 }
14
15 ColorGradient::ColorGradient(const Vector<ColorGradientKey>& keys)
16 {
17 setKeys(keys);
18 }
19
20 RGBA ColorGradient::evaluate(float t) const
21 {
22 if(mNumKeys == 0)
23 return 0;
24
25 if(mNumKeys == 1)
26 return mColors[0];
27
28 if(mDuration > 0.0f)
29 t = t / mDuration;
30
31 const uint32_t time = Bitwise::unormToUint<16>(Math::clamp01(t));
32
33 if(time < mTimes[0])
34 return mColors[0];
35
36 // Note: Add a version of evaluate that supports caching?
37 for(UINT32 i = 1; i < mNumKeys; i++)
38 {
39 const uint32_t curKeyTime = mTimes[i];
40 if(time > curKeyTime)
41 continue;
42
43 const uint32_t prevKeyTime = mTimes[i - 1];
44 const uint32_t fracColor = Bitwise::invLerpWord(prevKeyTime, curKeyTime, time) >> 8;
45 return Color::lerp(fracColor, mColors[i - 1], mColors[i]);
46 }
47
48 return mColors[mNumKeys - 1];
49 }
50
51 void ColorGradient::setKeys(const Vector<ColorGradientKey>& keys, float duration)
52 {
53#if BS_DEBUG_MODE
54 // Ensure keys are sorted
55 if(!keys.empty())
56 {
57 float time = keys[0].time;
58 for (UINT32 i = 1; i < (UINT32)keys.size(); i++)
59 {
60 assert(keys[i].time >= time);
61 time = keys[i].time;
62 }
63 }
64#endif
65
66 if(keys.size() > MAX_KEYS)
67 {
68 LOGWRN("Number of keys in ColorGradient exceeds the support number (" +
69 toString(MAX_KEYS) + "). Keys will be ignored.");
70 }
71
72 mDuration = duration;
73 mNumKeys = 0;
74
75 for(auto& key : keys)
76 {
77 if(mNumKeys >= MAX_KEYS)
78 break;
79
80 mColors[mNumKeys] = key.color.getAsRGBA();
81 mTimes[mNumKeys] = Bitwise::unormToUint<16>(Math::clamp01(key.time));
82
83 mNumKeys++;
84 }
85 }
86
87 Vector<ColorGradientKey> ColorGradient::getKeys() const
88 {
89 Vector<ColorGradientKey> output(mNumKeys);
90 for(UINT32 i = 0; i < mNumKeys; i++)
91 {
92 output[i].color = Color::fromRGBA(mColors[i]);
93 output[i].time = Bitwise::uintToUnorm<16>(mTimes[i]);
94 }
95
96 return output;
97 }
98
99 ColorGradientKey ColorGradient::getKey(UINT32 idx) const
100 {
101 if(idx >= mNumKeys)
102 return ColorGradientKey(Color::Black, 0.0f);
103
104 return ColorGradientKey(Color::fromRGBA(mColors[idx]), Bitwise::uintToUnorm<16>(mTimes[idx]));
105 }
106
107 void ColorGradient::setConstant(const Color& color)
108 {
109 mColors[0] = color.getAsRGBA();
110 mTimes[0] = 0;
111 mNumKeys = 1;
112 mDuration = 0.0f;
113 }
114
115 std::pair<float, float> ColorGradient::getTimeRange() const
116 {
117 if(mNumKeys == 0)
118 return std::make_pair(0.0f, 0.0f);
119
120 if(mNumKeys == 1)
121 {
122 float time = Bitwise::uintToUnorm<16>(mTimes[0]);
123 return std::make_pair(time, time);
124 }
125
126 return std::make_pair(
127 Bitwise::uintToUnorm<16>(mTimes[0]),
128 Bitwise::uintToUnorm<16>(mTimes[mNumKeys - 1])
129 );
130 }
131
132 bool ColorGradient::operator== (const ColorGradient& rhs) const
133 {
134 if (mNumKeys != rhs.mNumKeys || mDuration != rhs.mDuration)
135 return false;
136
137 for (uint32_t i = 0; i < mNumKeys; i++)
138 {
139 if (mColors[i] != rhs.mColors[i] || mTimes[i] != rhs.mTimes[i])
140 return false;
141 }
142
143 return true;
144 }
145}
146