1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2008  Douglas Boffey <dougaboffey@netscape.net>
4  *  Copyright (C) 2009  The Mana World Development Team
5  *  Copyright (C) 2009-2010  The Mana Developers
6  *  Copyright (C) 2011-2019  The ManaPlus Developers
7  *  Copyright (C) 2019-2021  Andrei Karas
8  *
9  *  This file is part of The ManaPlus Client.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #include "gui/palette.h"
26 
27 #include "utils/foreach.h"
28 #include "utils/timer.h"
29 
30 #ifndef USE_SDL2
31 #include <cmath>
32 #endif  // USE_SDL2
33 
34 #include "debug.h"
35 
36 const Color Palette::BLACK = Color(0, 0, 0, 255);
37 Palette::Palettes Palette::mInstances;
38 
39 const Color Palette::RAINBOW_COLORS[7] =
40 {
41     Color(255, 0, 0, 255),
42     Color(255, 153, 0, 255),
43     Color(255, 255, 0, 255),
44     Color(0, 153, 0, 255),
45     Color(0, 204, 204, 255),
46     Color(51, 0, 153, 255),
47     Color(153, 0, 153, 255)
48 };
49 
50 const int Palette::RAINBOW_COLOR_COUNT = 7;
51 
Palette(const int size)52 Palette::Palette(const int size) :
53     mRainbowTime(tick_time),
54     mColors(Colors(size)),
55     mCharColors(),
56     mGradVector()
57 {
58     mInstances.insert(this);
59 }
60 
~Palette()61 Palette::~Palette()
62 {
63     mInstances.erase(this);
64 }
65 
getCharColor(const signed char c,bool & valid) const66 const Color& Palette::getCharColor(const signed char c, bool &valid) const
67 {
68     const CharColors::const_iterator it = mCharColors.find(c);
69     if (it != mCharColors.end())
70     {
71         valid = true;
72         return mColors[(*it).second].color;
73     }
74 
75     valid = false;
76     return BLACK;
77 }
78 
advanceGradients()79 void Palette::advanceGradients()
80 {
81     FOR_EACH (Palettes::const_iterator, it, mInstances)
82         (*it)->advanceGradient();
83 }
84 
advanceGradient()85 void Palette::advanceGradient()
86 {
87     const int time = get_elapsed_time(mRainbowTime);
88     if (time > 5)
89     {
90         // For slower systems, advance can be greater than one (advance > 1
91         // skips advance-1 steps). Should make gradient look the same
92         // independent of the framerate.
93         const int advance = time / 5;
94 
95         for (size_t i = 0, sz = mGradVector.size(); i < sz; i++)
96         {
97             ColorElem *const elem A_NONNULLPOINTER = mGradVector[i];
98             if (elem == nullptr)
99                 continue;
100 
101             int delay = elem->delay;
102             const GradientTypeT &grad = elem->grad;
103 
104             if (grad == GradientType::PULSE)
105                 delay = delay / 20;
106 
107             const int numOfColors = (elem->grad == GradientType::SPECTRUM ? 6 :
108                 grad == GradientType::PULSE ? 127 :
109                 RAINBOW_COLOR_COUNT);
110 
111             elem->gradientIndex = (elem->gradientIndex + advance)
112                 % (delay * numOfColors);
113 
114             const int gradIndex = elem->gradientIndex;
115             const int pos = delay != 0 ? (gradIndex % delay) : gradIndex;
116             int colIndex;
117             if (delay != 0)
118                 colIndex = gradIndex / delay;
119             else
120                 colIndex = gradIndex;
121 
122             Color &color = elem->color;
123             int colVal;
124 
125             if (grad == GradientType::PULSE)
126             {
127                 colVal = CAST_S32(255.0 *
128                         sin(M_PI * colIndex / numOfColors));
129 
130                 const Color &col = elem->testColor;
131 
132                 color.r = ((colVal * col.r) / 255) % (col.r + 1);
133                 color.g = ((colVal * col.g) / 255) % (col.g + 1);
134                 color.b = ((colVal * col.b) / 255) % (col.b + 1);
135             }
136             else if (grad == GradientType::SPECTRUM)
137             {
138                 if ((colIndex % 2) != 0)
139                 {   // falling curve
140                     if (delay != 0)
141                     {
142                         colVal = CAST_S32(255.0 *
143                              (cos(M_PI * pos / delay) + 1) / 2);
144                     }
145                     else
146                     {
147                         colVal = CAST_S32(255.0 *
148                              (cos(M_PI * pos) + 1) / 2);
149                     }
150                 }
151                 else
152                 {   // ascending curve
153                     if (delay != 0)
154                     {
155                         colVal = CAST_S32(255.0 * (cos(M_PI *
156                             (delay - pos) / delay) + 1) / 2);
157                     }
158                     else
159                     {
160                         colVal = CAST_S32(255.0 * (cos(M_PI *
161                             (delay - pos)) + 1) / 2);
162                     }
163                 }
164 
165                 color.r = (colIndex == 0 || colIndex == 5) ? 255 :
166                     (colIndex == 1 || colIndex == 4) ? colVal : 0;
167                 color.g = (colIndex == 1 || colIndex == 2) ? 255 :
168                     (colIndex == 0 || colIndex == 3) ? colVal : 0;
169                 color.b = (colIndex == 3 || colIndex == 4) ? 255 :
170                     (colIndex == 2 || colIndex == 5) ? colVal : 0;
171             }
172             else if (elem->grad == GradientType::RAINBOW)
173             {
174                 const Color &startCol = RAINBOW_COLORS[colIndex];
175                 const Color &destCol
176                     = RAINBOW_COLORS[(colIndex + 1) % numOfColors];
177                 double startColVal;
178                 double destColVal;
179 
180                 if (delay != 0)
181                     startColVal = (cos(M_PI * pos / delay) + 1) / 2;
182                 else
183                     startColVal = 0;
184 
185                 destColVal = 1 - startColVal;
186 
187                 color.r = CAST_S32(startColVal
188                     * startCol.r + destColVal * destCol.r);
189 
190                 color.g = CAST_S32(startColVal
191                     * startCol.g + destColVal * destCol.g);
192 
193                 color.b = CAST_S32(startColVal
194                     * startCol.b + destColVal * destCol.b);
195             }
196         }
197 
198         mRainbowTime = tick_time;
199     }
200 }
201