]>
Commit | Line | Data |
---|---|---|
40989e46 WS |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: src/common/colourcmn.cpp | |
3 | // Purpose: wxColourBase implementation | |
4 | // Author: Francesco Montorsi | |
5 | // Modified by: | |
6 | // Created: 20/4/2006 | |
40989e46 WS |
7 | // Copyright: (c) Francesco Montorsi |
8 | // Licence: wxWindows licence | |
9 | ///////////////////////////////////////////////////////////////////////////// | |
10 | ||
11 | ||
12 | // For compilers that support precompilation, includes "wx.h". | |
13 | #include "wx/wxprec.h" | |
14 | ||
15 | #ifdef __BORLANDC__ | |
16 | #pragma hdrstop | |
17 | #endif | |
18 | ||
19 | #include "wx/colour.h" | |
20 | ||
e4db172a WS |
21 | #ifndef WX_PRECOMP |
22 | #include "wx/log.h" | |
7270921d | 23 | #include "wx/utils.h" |
dd05139a | 24 | #include "wx/gdicmn.h" |
193d0c93 | 25 | #include "wx/wxcrtvararg.h" |
e4db172a | 26 | #endif |
6b5d2431 | 27 | |
6f5d7825 RR |
28 | #if wxUSE_VARIANT |
29 | IMPLEMENT_VARIANT_OBJECT_EXPORTED(wxColour,WXDLLEXPORT) | |
30 | #endif | |
40989e46 | 31 | |
28953245 SC |
32 | |
33 | // ---------------------------------------------------------------------------- | |
34 | // XTI | |
35 | // ---------------------------------------------------------------------------- | |
36 | ||
37 | #if wxUSE_EXTENDED_RTTI | |
38 | ||
39 | #include <string.h> | |
40 | ||
41 | template<> void wxStringReadValue(const wxString &s, wxColour &data ) | |
42 | { | |
43 | if ( !data.Set(s) ) | |
44 | { | |
45 | wxLogError(_("String To Colour : Incorrect colour specification : %s"), | |
46 | s.c_str() ); | |
47 | data = wxNullColour; | |
48 | } | |
49 | } | |
50 | ||
51 | template<> void wxStringWriteValue(wxString &s, const wxColour &data ) | |
52 | { | |
53 | s = data.GetAsString(wxC2S_HTML_SYNTAX); | |
54 | } | |
55 | ||
56 | wxTO_STRING_IMP( wxColour ) | |
57 | wxFROM_STRING_IMP( wxColour ) | |
58 | ||
59 | wxIMPLEMENT_DYNAMIC_CLASS_WITH_COPY_AND_STREAMERS_XTI( wxColour, wxObject, \ | |
60 | "wx/colour.h", &wxTO_STRING( wxColour ), &wxFROM_STRING( wxColour )) | |
e765d7ee | 61 | //WX_IMPLEMENT_ANY_VALUE_TYPE(wxAnyValueTypeImpl<wxColour>) |
28953245 SC |
62 | wxBEGIN_PROPERTIES_TABLE(wxColour) |
63 | wxREADONLY_PROPERTY( Red, unsigned char, Red, wxEMPTY_PARAMETER_VALUE, \ | |
64 | 0 /*flags*/, wxT("Helpstring"), wxT("group")) | |
65 | wxREADONLY_PROPERTY( Green, unsigned char, Green, wxEMPTY_PARAMETER_VALUE, \ | |
66 | 0 /*flags*/, wxT("Helpstring"), wxT("group")) | |
67 | wxREADONLY_PROPERTY( Blue, unsigned char, Blue, wxEMPTY_PARAMETER_VALUE, \ | |
68 | 0 /*flags*/, wxT("Helpstring"), wxT("group")) | |
69 | wxEND_PROPERTIES_TABLE() | |
70 | ||
71 | wxDIRECT_CONSTRUCTOR_3( wxColour, unsigned char, Red, \ | |
72 | unsigned char, Green, unsigned char, Blue ) | |
73 | ||
74 | wxEMPTY_HANDLERS_TABLE(wxColour) | |
75 | #else | |
76 | ||
77 | #if wxCOLOUR_IS_GDIOBJECT | |
78 | wxIMPLEMENT_DYNAMIC_CLASS(wxColour, wxGDIObject) | |
79 | #else | |
80 | wxIMPLEMENT_DYNAMIC_CLASS(wxColour, wxObject) | |
81 | #endif | |
82 | ||
83 | #endif | |
84 | ||
40989e46 WS |
85 | // ============================================================================ |
86 | // wxString <-> wxColour conversions | |
87 | // ============================================================================ | |
88 | ||
e86d4e59 | 89 | bool wxColourBase::FromString(const wxString& str) |
40989e46 | 90 | { |
e86d4e59 | 91 | if ( str.empty() ) |
40989e46 WS |
92 | return false; // invalid or empty string |
93 | ||
b534968d | 94 | if ( wxStrnicmp(str, wxT("RGB"), 3) == 0 ) |
40989e46 | 95 | { |
7270921d | 96 | // CSS-like RGB specification |
b534968d | 97 | // according to http://www.w3.org/TR/css3-color/#colorunits |
7270921d | 98 | // values outside 0-255 range are allowed but should be clipped |
b534968d VZ |
99 | int red, green, blue, |
100 | alpha = wxALPHA_OPAQUE; | |
101 | if ( str.length() > 3 && (str[3] == wxT('a') || str[3] == wxT('A')) ) | |
102 | { | |
f0e52da8 VZ |
103 | // We can't use sscanf() for the alpha value as sscanf() uses the |
104 | // current locale while the floating point numbers in CSS always | |
105 | // use point as decimal separator, regardless of locale. So parse | |
106 | // the tail of the string manually by putting it in a buffer and | |
107 | // using wxString::ToCDouble() below. Notice that we can't use "%s" | |
108 | // for this as it stops at white space and we need "%c" to avoid | |
109 | // this and really get all the rest of the string into the buffer. | |
110 | ||
111 | const unsigned len = str.length(); // always big enough | |
112 | wxCharBuffer alphaBuf(len); | |
113 | char * const alphaPtr = alphaBuf.data(); | |
114 | ||
115 | for ( unsigned n = 0; n < len; n++ ) | |
116 | alphaPtr[n] = '\0'; | |
117 | ||
118 | // Construct the format string which ensures that the last argument | |
119 | // receives all the rest of the string. | |
120 | wxString formatStr; | |
121 | formatStr << wxS("( %d , %d , %d , %") << len << 'c'; | |
122 | ||
123 | // Notice that we use sscanf() here because if the string is not | |
124 | // ASCII it can't represent a valid RGB colour specification anyhow | |
125 | // and like this we can be sure that %c corresponds to "char *" | |
126 | // while with wxSscanf() it depends on the type of the string | |
127 | // passed as first argument: if it is a wide string, then %c | |
128 | // expects "wchar_t *" matching parameter under MSW for example. | |
129 | if ( sscanf(str.c_str() + 4, | |
3130a8d0 | 130 | formatStr.mb_str(), |
f0e52da8 VZ |
131 | &red, &green, &blue, alphaPtr) != 4 ) |
132 | return false; | |
133 | ||
134 | // Notice that we must explicitly specify the length to get rid of | |
135 | // trailing NULs. | |
136 | wxString alphaStr(alphaPtr, wxStrlen(alphaPtr)); | |
137 | if ( alphaStr.empty() || alphaStr.Last() != ')' ) | |
138 | return false; | |
139 | ||
140 | alphaStr.RemoveLast(); | |
141 | alphaStr.Trim(); | |
142 | ||
143 | double a; | |
144 | if ( !alphaStr.ToCDouble(&a) ) | |
b534968d VZ |
145 | return false; |
146 | ||
147 | alpha = wxRound(a * 255); | |
148 | } | |
149 | else // no 'a' following "rgb" | |
150 | { | |
151 | if ( wxSscanf(str.wx_str() + 3, wxT("( %d , %d , %d )"), | |
152 | &red, &green, &blue) != 3 ) | |
153 | return false; | |
154 | } | |
155 | ||
156 | Set((unsigned char)wxClip(red, 0, 255), | |
157 | (unsigned char)wxClip(green, 0, 255), | |
158 | (unsigned char)wxClip(blue, 0, 255), | |
159 | (unsigned char)wxClip(alpha, 0, 255)); | |
40989e46 WS |
160 | } |
161 | else if ( str[0] == wxT('#') && wxStrlen(str) == 7 ) | |
162 | { | |
163 | // hexadecimal prefixed with # (HTML syntax) | |
164 | unsigned long tmp; | |
52de37c7 | 165 | if (wxSscanf(str.wx_str() + 1, wxT("%lx"), &tmp) != 1) |
40989e46 WS |
166 | return false; |
167 | ||
04407723 PC |
168 | Set((unsigned char)(tmp >> 16), |
169 | (unsigned char)(tmp >> 8), | |
170 | (unsigned char)tmp); | |
40989e46 WS |
171 | } |
172 | else if (wxTheColourDatabase) // a colour name ? | |
173 | { | |
174 | // we can't do | |
175 | // *this = wxTheColourDatabase->Find(str) | |
176 | // because this place can be called from constructor | |
177 | // and 'this' could not be available yet | |
178 | wxColour clr = wxTheColourDatabase->Find(str); | |
a1b806b9 | 179 | if (clr.IsOk()) |
81853b98 WS |
180 | Set((unsigned char)clr.Red(), |
181 | (unsigned char)clr.Green(), | |
182 | (unsigned char)clr.Blue()); | |
40989e46 WS |
183 | } |
184 | ||
a1b806b9 | 185 | if (IsOk()) |
40989e46 WS |
186 | return true; |
187 | ||
188 | wxLogDebug(wxT("wxColour::Set - couldn't set to colour string '%s'"), str); | |
189 | return false; | |
190 | } | |
191 | ||
192 | wxString wxColourBase::GetAsString(long flags) const | |
193 | { | |
194 | wxString colName; | |
195 | ||
b534968d | 196 | const bool isOpaque = Alpha() == wxALPHA_OPAQUE; |
40989e46 | 197 | |
b534968d VZ |
198 | // we can't use the name format if the colour is not opaque as the alpha |
199 | // information would be lost | |
200 | if ( (flags & wxC2S_NAME) && isOpaque ) | |
40989e46 | 201 | { |
b534968d | 202 | colName = wxTheColourDatabase->FindName( |
5c33522f | 203 | static_cast<const wxColour &>(*this)).MakeLower(); |
40989e46 | 204 | } |
b534968d VZ |
205 | |
206 | if ( colName.empty() ) | |
40989e46 | 207 | { |
b534968d VZ |
208 | const int red = Red(), |
209 | blue = Blue(), | |
210 | green = Green(); | |
211 | ||
212 | if ( flags & wxC2S_CSS_SYNTAX ) | |
213 | { | |
214 | // no name for this colour; return it in CSS syntax | |
215 | if ( isOpaque ) | |
216 | { | |
217 | colName.Printf(wxT("rgb(%d, %d, %d)"), red, green, blue); | |
218 | } | |
219 | else // use rgba() form | |
220 | { | |
f0e52da8 VZ |
221 | colName.Printf(wxT("rgba(%d, %d, %d, %s)"), |
222 | red, green, blue, | |
223 | wxString::FromCDouble(Alpha() / 255., 3)); | |
b534968d VZ |
224 | } |
225 | } | |
226 | else if ( flags & wxC2S_HTML_SYNTAX ) | |
227 | { | |
228 | wxASSERT_MSG( isOpaque, "alpha is lost in HTML syntax" ); | |
229 | ||
230 | // no name for this colour; return it in HTML syntax | |
231 | colName.Printf(wxT("#%02X%02X%02X"), red, green, blue); | |
232 | } | |
40989e46 WS |
233 | } |
234 | ||
b534968d | 235 | // this function should alway returns a non-empty string |
40989e46 WS |
236 | wxASSERT_MSG(!colName.empty(), |
237 | wxT("Invalid wxColour -> wxString conversion flags")); | |
238 | ||
239 | return colName; | |
240 | } | |
241 | ||
198c264d | 242 | // static |
ce00f59b | 243 | void wxColourBase::MakeMono(unsigned char* r, unsigned char* g, unsigned char* b, |
198c264d JS |
244 | bool on) |
245 | { | |
246 | *r = *g = *b = on ? 255 : 0; | |
247 | } | |
248 | ||
249 | // static | |
250 | void wxColourBase::MakeGrey(unsigned char* r, unsigned char* g, unsigned char* b | |
251 | /*, unsigned char brightness */ | |
252 | ) | |
253 | { | |
254 | *r = *g = *b = (wxByte)(((*b)*117UL + (*g)*601UL + (*r)*306UL) >> 10); | |
255 | } | |
256 | ||
257 | // static | |
258 | void wxColourBase::MakeGrey(unsigned char* r, unsigned char* g, unsigned char* b, | |
259 | double weight_r, double weight_g, double weight_b) | |
260 | { | |
261 | double luma = (*r) * weight_r + (*g) * weight_g + (*b) * weight_b; | |
262 | *r = *g = *b = (wxByte)wxRound(luma); | |
263 | } | |
264 | ||
265 | // static | |
ce00f59b | 266 | void wxColourBase::MakeDisabled(unsigned char* r, unsigned char* g, unsigned char* b, |
198c264d JS |
267 | unsigned char brightness) |
268 | { | |
269 | //MakeGrey(r, g, b, brightness); // grey no-blend version | |
270 | *r = AlphaBlend(*r, brightness, 0.4); | |
271 | *g = AlphaBlend(*g, brightness, 0.4); | |
272 | *b = AlphaBlend(*b, brightness, 0.4); | |
273 | } | |
274 | ||
893d540e VZ |
275 | wxColour& wxColourBase::MakeDisabled(unsigned char brightness) |
276 | { | |
277 | unsigned char r = Red(), | |
278 | g = Green(), | |
279 | b = Blue(); | |
280 | MakeDisabled(&r, &g, &b, brightness); | |
281 | Set(r, g, b, Alpha()); | |
282 | return static_cast<wxColour&>(*this); | |
283 | } | |
284 | ||
198c264d JS |
285 | // AlphaBlend is used by ChangeLightness and MakeDisabled |
286 | ||
287 | // static | |
ce00f59b | 288 | unsigned char wxColourBase::AlphaBlend(unsigned char fg, unsigned char bg, |
198c264d JS |
289 | double alpha) |
290 | { | |
291 | double result = bg + (alpha * (fg - bg)); | |
292 | result = wxMax(result, 0.0); | |
293 | result = wxMin(result, 255.0); | |
294 | return (unsigned char)result; | |
295 | } | |
296 | ||
297 | // ChangeLightness() is a utility function that simply darkens | |
298 | // or lightens a color, based on the specified percentage | |
299 | // ialpha of 0 would be completely black, 100 completely white | |
300 | // an ialpha of 100 returns the same colour | |
301 | ||
302 | // static | |
303 | void wxColourBase::ChangeLightness(unsigned char* r, unsigned char* g, unsigned char* b, | |
304 | int ialpha) | |
305 | { | |
306 | if (ialpha == 100) return; | |
307 | ||
308 | // ialpha is 0..200 where 0 is completely black | |
309 | // and 200 is completely white and 100 is the same | |
310 | // convert that to normal alpha 0.0 - 1.0 | |
311 | ialpha = wxMax(ialpha, 0); | |
312 | ialpha = wxMin(ialpha, 200); | |
313 | double alpha = ((double)(ialpha - 100.0))/100.0; | |
314 | ||
315 | unsigned char bg; | |
316 | if (ialpha > 100) | |
317 | { | |
318 | // blend with white | |
319 | bg = 255; | |
320 | alpha = 1.0 - alpha; // 0 = transparent fg; 1 = opaque fg | |
321 | } | |
322 | else | |
323 | { | |
324 | // blend with black | |
325 | bg = 0; | |
326 | alpha = 1.0 + alpha; // 0 = transparent fg; 1 = opaque fg | |
327 | } | |
328 | ||
329 | *r = AlphaBlend(*r, bg, alpha); | |
330 | *g = AlphaBlend(*g, bg, alpha); | |
331 | *b = AlphaBlend(*b, bg, alpha); | |
332 | } | |
333 | ||
334 | wxColour wxColourBase::ChangeLightness(int ialpha) const | |
335 | { | |
336 | wxByte r = Red(); | |
337 | wxByte g = Green(); | |
338 | wxByte b = Blue(); | |
339 | ChangeLightness(&r, &g, &b, ialpha); | |
340 | return wxColour(r,g,b); | |
341 | } | |
342 | ||
7d01c54d WS |
343 | #if WXWIN_COMPATIBILITY_2_6 |
344 | ||
345 | // static | |
40989e46 WS |
346 | wxColour wxColourBase::CreateByName(const wxString& name) |
347 | { | |
348 | return wxColour(name); | |
349 | } | |
7d01c54d WS |
350 | |
351 | void wxColourBase::InitFromName(const wxString& col) | |
352 | { | |
353 | Set(col); | |
354 | } | |
355 | ||
356 | #endif // WXWIN_COMPATIBILITY_2_6 | |
8b51786f VZ |
357 | |
358 | // wxColour <-> wxString utilities, used by wxConfig | |
359 | wxString wxToString(const wxColourBase& col) | |
360 | { | |
361 | return col.IsOk() ? col.GetAsString(wxC2S_CSS_SYNTAX) | |
362 | : wxString(); | |
363 | } | |
364 | ||
365 | bool wxFromString(const wxString& str, wxColourBase *col) | |
366 | { | |
9a83f860 | 367 | wxCHECK_MSG( col, false, wxT("NULL output parameter") ); |
8b51786f VZ |
368 | |
369 | if ( str.empty() ) | |
370 | { | |
371 | *col = wxNullColour; | |
372 | return true; | |
373 | } | |
374 | ||
375 | return col->Set(str); | |
376 | } | |
377 | ||
378 |