1 /*
2    Copyright (C) 2003 - 2018 by the Battle for Wesnoth Project https://www.wesnoth.org/
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY.
10 
11    See the COPYING file for more details.
12 */
13 
14 #pragma once
15 
16 #include "global.hpp"
17 
18 #include <algorithm> // for max
19 #include <cstdint>
20 #include <ostream>
21 #include <string>
22 #include <utility>
23 
24 struct SDL_Color;
25 
26 //
27 // TODO: constexpr
28 //
29 
30 const uint32_t SDL_ALPHA_MASK = 0xFF000000;
31 const uint32_t SDL_RED_MASK   = 0x00FF0000;
32 const uint32_t SDL_GREEN_MASK = 0x0000FF00;
33 const uint32_t SDL_BLUE_MASK  = 0x000000FF;
34 
35 const uint32_t SDL_ALPHA_BITSHIFT = 24;
36 const uint32_t SDL_RED_BITSHIFT   = 16;
37 const uint32_t SDL_GREEN_BITSHIFT = 8;
38 const uint32_t SDL_BLUE_BITSHIFT  = 0;
39 
40 const uint32_t RGBA_ALPHA_MASK = 0x000000FF;
41 const uint32_t RGBA_RED_MASK   = 0xFF000000;
42 const uint32_t RGBA_GREEN_MASK = 0x00FF0000;
43 const uint32_t RGBA_BLUE_MASK  = 0x0000FF00;
44 
45 const uint32_t RGBA_ALPHA_BITSHIFT = 0;
46 const uint32_t RGBA_RED_BITSHIFT   = 24;
47 const uint32_t RGBA_GREEN_BITSHIFT = 16;
48 const uint32_t RGBA_BLUE_BITSHIFT  = 8;
49 
50 const uint8_t ALPHA_OPAQUE = 255;
51 
52 struct color_t
53 {
color_tcolor_t54 	color_t()
55 		: r(255)
56 		, g(255)
57 		, b(255)
58 		, a(ALPHA_OPAQUE)
59 	{}
60 
color_tcolor_t61 	color_t(uint8_t r_val, uint8_t g_val, uint8_t b_val, uint8_t a_val = ALPHA_OPAQUE)
62 		: r(r_val)
63 		, g(g_val)
64 		, b(b_val)
65 		, a(a_val)
66 	{}
67 
68 	// Implemented in sdl/utils.cpp to avoid dependency nightmares
69 	explicit color_t(const SDL_Color& c);
70 
71 	/**
72 	 * Creates a new color_t object from a string variable in "R,G,B,A" format.
73 	 * An empty string results in white. Otherwise, omitting components other than
74 	 * alpha is an error.
75 	 *
76 	 * @param c      A string variable, in "R,G,B,A" format.
77 	 * @return       A new color_t object.
78 	 *
79 	 * @throw        std::invalid_argument if the string is not correctly formatted
80 	 */
81 	static color_t from_rgba_string(const std::string& c);
82 
83 	/**
84 	 * Creates a new opaque color_t object from a string variable in "R,G,B" format.
85 	 * An empty string results in white. Otherwise, omitting components is an error.
86 	 *
87 	 * @param c      A string variable, in "R,G,B" format.
88 	 * @return       A new color_t object.
89 	 *
90 	 * @throw        std::invalid_argument if the string is not correctly formatted
91 	 */
92 	static color_t from_rgb_string(const std::string& c);
93 
94 	/**
95 	 * Creates a new color_t object from a string variable in hex format.
96 	 *
97 	 * @param c      A string variable, in rrggbb hex format.
98 	 * @return       A new color_t object.
99 	 *
100 	 * @throw        std::invalid_argument if the string is not correctly formatted
101 	 */
102 	static color_t from_hex_string(const std::string& c);
103 
104 	/**
105 	 * Creates a new color_t object from a uint32_t variable.
106 	 *
107 	 * @param c      A uint32_t variable, in RGBA format.
108 	 * @return       A new color_t object.
109 	 */
110 	static color_t from_rgba_bytes(uint32_t c);
111 
112 	/**
113 	 * Creates a new color_t object from a uint32_t variable.
114 	 *
115 	 * @param c      A uint32_t variable, in ARGB format.
116 	 * @return       A new color_t object.
117 	 */
118 	static color_t from_argb_bytes(uint32_t c);
119 
120 	/**
121 	 * Returns the stored color in rrggbb hex format.
122 	 *
123 	 * @return       The string in hex format. The preceding '#' needed for pango markup
124 	 *               is prepended.
125 	 */
126 	std::string to_hex_string() const;
127 
128 	/**
129 	 * Returns the stored color as a uint32_t, in RGBA format.
130 	 *
131 	 * @return       The new uint32_t object.
132 	 */
to_rgba_bytescolor_t133 	uint32_t to_rgba_bytes() const
134 	{
135 		return
136 			(static_cast<uint32_t>(r) << RGBA_RED_BITSHIFT) |
137 			(static_cast<uint32_t>(g) << RGBA_GREEN_BITSHIFT) |
138 			(static_cast<uint32_t>(b) << RGBA_BLUE_BITSHIFT) |
139 			(static_cast<uint32_t>(a) << RGBA_ALPHA_BITSHIFT);
140 	}
141 
142 	/**
143 	 * Returns the stored color as a uint32_t, an ARGB format.
144 	 *
145 	 * @return       The new uint32_t object.
146 	 */
to_argb_bytescolor_t147 	uint32_t to_argb_bytes() const
148 	{
149 		return
150 			(static_cast<uint32_t>(r) << SDL_RED_BITSHIFT) |
151 			(static_cast<uint32_t>(g) << SDL_GREEN_BITSHIFT) |
152 			(static_cast<uint32_t>(b) << SDL_BLUE_BITSHIFT) |
153 			(static_cast<uint32_t>(a) << SDL_ALPHA_BITSHIFT);
154 	}
155 
156 	/**
157 	 * Returns the stored color as an "R,G,B,A" string
158 	 *
159 	 * @return      The new color string.
160 	 */
161 	std::string to_rgba_string() const;
162 
163 	/**
164 	 * Returns the stored color as an "R,G,B" string
165 	 *
166 	 * @return      The new color string.
167 	 */
168 	std::string to_rgb_string() const;
169 
170 	/**
171 	 * Returns the stored color as an color_t object.
172 	 *
173 	 * @return       The new color_t object.
174 	 */
175 	// Implemented in sdl/utils.cpp to avoid dependency nightmares
176 	SDL_Color to_sdl() const;
177 
178 	/** Red value */
179 	uint8_t r;
180 
181 	/** Green value */
182 	uint8_t g;
183 
184 	/** Blue value */
185 	uint8_t b;
186 
187 	/** Alpha value */
188 	uint8_t a;
189 
nullcolor_t190 	bool null() const
191 	{
192 		return *this == null_color();
193 	}
194 
operator ==color_t195 	bool operator==(const color_t& c) const
196 	{
197 		return r == c.r && g == c.g && b == c.b && a == c.a;
198 	}
199 
operator !=color_t200 	bool operator!=(const color_t& c) const
201 	{
202 		return !(*this == c);
203 	}
204 
blend_addcolor_t205 	color_t blend_add(const color_t& c) const
206 	{
207 		// Do some magic to detect integer overflow
208 		// We want overflows to max out the component instead of wrapping.
209 		// The static_cast is to silence narrowing conversion warnings etc
210 		return {
211 			static_cast<uint8_t>(r > 255 - c.r ? 255 : r + c.r),
212 			static_cast<uint8_t>(g > 255 - c.g ? 255 : g + c.g),
213 			static_cast<uint8_t>(b > 255 - c.b ? 255 : b + c.b),
214 			static_cast<uint8_t>(a > 255 - c.a ? 255 : a + c.a),
215 		};
216 	}
217 
blend_lightencolor_t218 	color_t blend_lighten(const color_t& c) const
219 	{
220 		return {
221 			std::max<uint8_t>(r, c.r),
222 			std::max<uint8_t>(g, c.g),
223 			std::max<uint8_t>(b, c.b),
224 			std::max<uint8_t>(a, c.a),
225 		};
226 	}
227 
inversecolor_t228 	color_t inverse() const {
229 		return {
230 			static_cast<uint8_t>(255 - r),
231 			static_cast<uint8_t>(255 - g),
232 			static_cast<uint8_t>(255 - b),
233 			a
234 		};
235 	}
236 
237 	/** Definition of a 'null' color - fully transparent black. */
null_colorcolor_t238 	static color_t null_color()
239 	{
240 		return {0,0,0,0};
241 	}
242 };
243 
operator <<(std::ostream & s,const color_t & c)244 inline std::ostream& operator<<(std::ostream& s, const color_t& c)
245 {
246 	s << static_cast<int>(c.r) << " "
247 	  << static_cast<int>(c.g) << " "
248 	  << static_cast<int>(c.b) << " "
249 	  << static_cast<int>(c.a) << std::endl;
250 
251 	return s;
252 }
253 
254 namespace std
255 {
256 	template<>
257 	struct hash<color_t>
258 	{
operator ()std::hash259 		size_t operator()(const color_t& c) const NOEXCEPT
260 		{
261 			return c.to_rgba_bytes();
262 		}
263 	};
264 }
265