PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PColor.cpp
Go to the documentation of this file.
1//* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
3// Name: src/objects/PColor.cpp
4// Purpose: Contains lightweight specification for a color
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2003-2026 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9//
10//
11// This file is part of the PEBL project.
12//
13// PEBL is free software; you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation; either version 2 of the License, or
16// (at your option) any later version.
17//
18// PEBL is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with PEBL; if not, write to the Free Software
25// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
28#include "PColor.h"
29#include "../base/Variant.h"
30#include "../base/PEBLObject.h"
31#include "RGBColorNames.h"
32#include "../utility/PEBLUtility.h"
33#include "../utility/PError.h"
34
35
36#include <string>
37#include <cstring>
38
39using std::string;
40using std::cout;
41using std::endl;
43
46 mRed(0),
47 mGreen(0),
48 mBlue(0),
49 mAlpha(255),
50 mChanged(false)
51
52{
53
54 InitializeProperty("NAME", Variant("<COLOR>"));
59
60}
61
62PColor::PColor(unsigned int color):
63 mRed(0),mGreen(0),mBlue(0),mAlpha(0),mChanged(false)
64 {
65 SetColorByRGBA(color);
66 };
67
68
70PColor::PColor(int red, int green, int blue, int alpha):
72 mRed(To8BitColor(red)),
73 mGreen(To8BitColor(green)),
74 mBlue(To8BitColor(blue)),
75 mAlpha(To8BitColor(alpha)),
76 mChanged(false)
77
78{
79 InitializeProperty("NAME", Variant("<COLOR>"));
84
85}
86
88PColor::PColor(const string & colorname):
90 mRed(0),
91 mGreen(0),
92 mBlue(0),
93 mAlpha(255),
94 mChanged(false)
95{
96 InitializeProperty("NAME", Variant("<COLOR>"));
98 InitializeProperty("GREEN",Variant(0));
99 InitializeProperty("BLUE",Variant(0));
100 InitializeProperty("ALPHA",Variant(0));
101 // cout << "Making color by name:" << colorname << endl;
102 SetColorByName(colorname);
103
104 // Only set alpha to 255 if it wasn't explicitly set by an 8-character hex code (#RRGGBBAA)
105 // Check if colorname is a hex code with alpha channel
106 bool hasExplicitAlpha = false;
107 if (!colorname.empty() && colorname[0] == '#' && colorname.length() == 9) {
108 // Hex code with alpha - alpha was already set by SetColorByName
109 hasExplicitAlpha = true;
110 }
111 if (!hasExplicitAlpha) {
112 SetAlpha(255);
113 }
114
115 mChanged = false; // Clear flag after construction
116}
117
118
120PColor::PColor(const PColor & pcolor):
122{
123
124 mRed = pcolor.GetRed();
125 mGreen = pcolor.GetGreen();
126 mBlue = pcolor.GetBlue();
127 mAlpha = pcolor.GetAlpha();
128 mChanged = false;
129
130 InitializeProperty("NAME", Variant("<COLOR>"));
131 InitializeProperty("RED",Variant((int)mRed));
132 InitializeProperty("GREEN",Variant((int)mGreen));
133 InitializeProperty("BLUE",Variant((int)mBlue));
134 InitializeProperty("ALPHA",Variant((int)mAlpha));
135
136}
137
139{
140}
141
142void PColor::__SetProps__()
143{
148}
149
150
151
152
153bool PColor::SetProperty(std::string name, Variant v)
154{
155
156 if(name == "RED") SetRed(v);
157 else if(name == "GREEN") SetGreen(v);
158 else if(name == "BLUE") SetBlue(v);
159 else if(name == "ALPHA") SetAlpha(v);
160 else return false;
161
162 return true;
163}
164
165
166
168{
170
171 if(!v.IsNumber()) return OVE_INVALID_PROPERTY_TYPE;
172 if( v < Variant(0) || v > Variant(255)) return OVE_INVALID_PROPERTY_VALUE;
173 return OVE_SUCCESS;
174}
175
176
178 {
180 }
181
182
183
184
187int PColor::To8BitColor(int color)
188{
189 if(color<0) color = 0;
190 if(color>255) color = 255;
191
192 return color;
193}
194
195
196 //These will set a single color
197void PColor::SetRed (int color)
198{
199 mRed = To8BitColor(color);
201 mChanged = true;
202}
203
204void PColor::SetGreen(int color)
205{
206 mGreen = To8BitColor(color);
208 mChanged = true;
209}
210
211void PColor::SetBlue (int color)
212{
213 mBlue = To8BitColor(color);
215 mChanged = true;
216}
217
218void PColor::SetAlpha(int color)
219{
220 mAlpha = To8BitColor(color);
222 mChanged = true;
223}
224
225 //These will return a single color
226int PColor::GetRed () const { return mRed;}
227int PColor::GetGreen() const { return mGreen;}
228int PColor::GetBlue () const { return mBlue;}
229int PColor::GetAlpha() const { return mAlpha;}
230
231
235void PColor::SetColorByRGBA(unsigned int color)
236{
237 mAlpha = color % 255;
238 mBlue = color / 255 % 255;
239 mGreen = color / (255 * 255) % 255;
240 mRed = color / ( 255 * 255 * 255) % 255;
241 __SetProps__();
242}
243
244
245
247void PColor::SetColorByRGBA(int red, int green, int blue, int alpha)
248{
249 mRed = To8BitColor(red);
250 mGreen = To8BitColor(green);
251 mBlue = To8BitColor(blue);
252 mAlpha = To8BitColor(alpha);
253 __SetProps__();
254}
255
256
257void PColor::SetColorByName(const string & colorname)
258{
259
260 // Check if this is a hex color code (starts with #)
261 if (!colorname.empty() && colorname[0] == '#')
262 {
263 std::string hexcode = colorname.substr(1); // Remove the '#'
264
265 // Validate that hexcode contains only hex digits (0-9, A-F, a-f)
266 bool valid = true;
267 for (size_t i = 0; i < hexcode.length(); i++) {
268 char c = hexcode[i];
269 if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) {
270 valid = false;
271 break;
272 }
273 }
274
275 if (valid && (hexcode.length() == 3 || hexcode.length() == 6 || hexcode.length() == 8))
276 {
277 int r = 0, g = 0, b = 0, a = 255;
278
279 if (hexcode.length() == 3) {
280 // #RGB format - expand to #RRGGBB
281 r = std::stoi(hexcode.substr(0, 1), nullptr, 16) * 17; // 0xF becomes 0xFF
282 g = std::stoi(hexcode.substr(1, 1), nullptr, 16) * 17;
283 b = std::stoi(hexcode.substr(2, 1), nullptr, 16) * 17;
284 }
285 else if (hexcode.length() == 6) {
286 // #RRGGBB format
287 r = std::stoi(hexcode.substr(0, 2), nullptr, 16);
288 g = std::stoi(hexcode.substr(2, 2), nullptr, 16);
289 b = std::stoi(hexcode.substr(4, 2), nullptr, 16);
290 }
291 else if (hexcode.length() == 8) {
292 // #RRGGBBAA format
293 r = std::stoi(hexcode.substr(0, 2), nullptr, 16);
294 g = std::stoi(hexcode.substr(2, 2), nullptr, 16);
295 b = std::stoi(hexcode.substr(4, 2), nullptr, 16);
296 a = std::stoi(hexcode.substr(6, 2), nullptr, 16);
297 }
298
299 mRed = To8BitColor(r);
300 mGreen = To8BitColor(g);
301 mBlue = To8BitColor(b);
302 mAlpha = To8BitColor(a);
303 __SetProps__();
304 return; // Successfully parsed hex color
305 }
306 }
307
308 std::string ucasename = PEBLUtility::ToUpper(colorname);
309
310
313 int bottom = 0;
314 int top = RGBNames::NumRGBColorNames-1;
315 int mid = (top + bottom)/2;
316 int foundindex = -1;
317 int test;
318 bool sep1=false; //This flags if the separation between top and bottom is 1.
319
320
321 //Check the top-most name first. If this is a hit, we could get stuck
322 if(ucasename == RGBNames::ColorNames[top].name)
323 {
324 foundindex = top; //It is a hit.
325 }
326
327 //Check the bottom-most name. If this is a hit, we could get stuck
328 if(ucasename == RGBNames::ColorNames[bottom].name)
329 {
330 foundindex = bottom; //It is a hit.
331 }
332
333
334 //repeat until you find it or there is nothing to find.
335 while(((top - bottom) > 1) && (foundindex == -1) && !sep1)
336 {
337
338
339 mid = (top + bottom)/2;
340
341 //set the '1-item separation' flag to true.
342 if((top - bottom) == 1) sep1 = true;
343
344
345 //Compare the library color and the current color. 0 means they are the same.
346 test =strcmp(ucasename.c_str(), RGBNames::ColorNames[mid].name.c_str());
347
348
349 if(test < 0) //If test < 0, the chosen word comes before the point in the dictionary. Move top down.
350 {
351 top = mid;
352 }
353 else if(test > 0) //If test > 0, the chosen word comes after the point in the dictionary. Move bottom up.
354 {
355 bottom = mid;
356 }
357 else
358 {
359 foundindex = mid;
360 }
361
362
363 }
364
365 //Check top & bottom to see if there is a match.
366 //if(strcmp(ucasename, RGBNames::ColorNames[top].name) == 0) foundindex = top;
367 //if(strcmp(ucasename, RGBNames::ColorNames[bottom].name) == 0) foundindex = bottom;
368
369
370 //If foundindex is still -1, we did not find the color name in the library.
371 if(foundindex == -1)
372 {
373 PError::SignalWarning("Color name [" + string(ucasename) + "] not found. Using Black.");
374 foundindex = 25; //25 is the index of black.
375
376 //Better error detection should be done here: searching with levenshtien distance,
377 //ignoring _ or - or #s, etc.
378 }
379
380
381
382 mRed = RGBNames::ColorNames[foundindex].r;
383 mGreen = RGBNames::ColorNames[foundindex].g;
384 mBlue = RGBNames::ColorNames[foundindex].b;
385 mAlpha = 255;
386 __SetProps__();
387
388 // cout << mRed << "-" << mGreen << "-" << mBlue << "-" <<endl;
389}
390
391
392//This will return an unsigned int that encapsulates the RGBA values into a 32-bit number.
393//It does this using *, which might be slower than bit-shifting/masking, but
395unsigned int PColor::GetColor() const
396{
397 unsigned int color=0;
398 color = mAlpha + (255 * mBlue) + (255 * 255 * mGreen) + (255 * 255 * 255 * mRed);
399 return color;
400}
401
402
403
404
406std::ostream & PColor::SendToStream(std::ostream& out) const
407{
408 out << ObjectName() << std::flush;
409 return out;
410}
411
412std::string PColor::ObjectName() const
413{
414 Variant tmp = "<PColor: Red: [";
415
416 tmp = tmp + Variant((int)mRed) ;
417 tmp = tmp + Variant("] Green: [");
418 tmp = tmp + Variant((int)mGreen);
419 tmp = tmp + Variant("] Blue: [");
420 tmp = tmp + Variant((int)mBlue );
421 tmp = tmp + Variant("] Alpha: [");
422 tmp = tmp + Variant((int)mAlpha );
423 tmp = tmp + Variant("] Color Code: [");
424 tmp = tmp + Variant((long unsigned int)GetColor());
425 tmp = tmp + Variant("]>");
426
427 return tmp.GetString();
428}
ObjectValidationError
Definition PEBLObject.h:37
@ OVE_SUCCESS
Definition PEBLObject.h:38
@ OVE_INVALID_PROPERTY_VALUE
Definition PEBLObject.h:42
@ OVE_INVALID_PROPERTY_NAME
Definition PEBLObject.h:40
@ OVE_INVALID_PROPERTY_TYPE
Definition PEBLObject.h:41
@ CDT_COLOR
Definition PEBLObject.h:49
void SetRed(int color)
Definition PColor.cpp:197
int GetRed() const
Definition PColor.cpp:226
virtual std::ostream & SendToStream(std::ostream &out) const
This sends the color descriptions to the specified stream.
Definition PColor.cpp:406
virtual ObjectValidationError ValidateProperty(std::string name, Variant v) const
Definition PColor.cpp:167
void SetColorByName(const std::string &colorname)
Sets color as by its name.
Definition PColor.cpp:257
virtual ~PColor()
Copy constructor.
Definition PColor.cpp:138
int GetAlpha() const
Definition PColor.cpp:229
PColor()
Standard constructor of PColor.
Definition PColor.cpp:44
virtual bool SetProperty(std::string, Variant v)
Standard Destructor.
Definition PColor.cpp:153
int GetBlue() const
Definition PColor.cpp:228
int GetGreen() const
Definition PColor.cpp:227
virtual std::string ObjectName() const
Definition PColor.cpp:412
unsigned int GetColor() const
Gets 32-bit unsigned int color.
Definition PColor.cpp:395
void SetGreen(int color)
Definition PColor.cpp:204
void SetColorByRGBA(unsigned int color)
Sets color as a 32-bit unsigned int.
Definition PColor.cpp:235
void SetBlue(int color)
Definition PColor.cpp:211
void SetAlpha(int color)
Definition PColor.cpp:218
virtual bool InitializeProperty(std::string name, Variant v)
virtual bool SetProperty(std::string name, Variant v)
virtual ObjectValidationError ValidateProperty(std::string, Variant v) const
std::string GetString() const
Definition Variant.cpp:1056
bool IsNumber() const
This tests whether the Variant is a number (i.e., a float or an integer.)
Definition Variant.cpp:930
std::string ToUpper(const std::string &text)
void SignalWarning(const std::string &message)
Definition PError.cpp:119
const int NumRGBColorNames
PEBL_RGB_Color_Names ColorNames[]