Reputation: 335
I am having trouble determining which class structure to use for storing RGB/RGBA color information.
I am making a very simple 3D game engine for fun and to hone my OO-programming abilities.
I would like my engine to have support for both RGB and RGBA colors. On top of that, I would like to have it so RGB and RGBA values can be specified as either floats clamped to [0, 1] or unsigned chars. I figured that for storage purposes, it would be better to use unsigned chars as they use a fourth as much memory as floats.
So now, I have devised the following classes to use for this: Color, Color3, Color3f, Color3c, Color4, Color4f, and Color4c. Color is the superclass of Color3 and Color4, while Color3 is the superclass of Color3f and Color3c, as Color4 is with Color4f and Color4c.
This seems like a much more complicated approach than need be. I also thought for a while about using generics, so instead of Color3f and Color3c, one would use Color3<float> and Color3<unsigned char>. However, this approach didn't seem right either.
Ideally, I would like to be able to write code like:
Color3 red1(255, 0, 0);
Color3 red2(1.0f, 0.0f, 0.0f);
Color* someColor = new Color3(1.0f, 0.0f, 0.5f);
Color4 anotherColor = *someColor;
Color4 thisColor = anotherColor.toChar();
//thisColor.r = 255, thisColor.g = 0, thisColor.b = 127
thisColor = thisColor.toFloat();
//thisColor.r = 1.0f, thisColor.g = 0.0f, thisColor.b = 0.5f
Now this can't be implemented (at least to my knowledge) in C++, but how could I muster the same functionality without creating 7 separate classes? And without storing wasted information? For example, picture a 1024x1024 image in memory. That would be an array with over a million of these Colors, so how can I make a Color class hierarchy that is flexible and reusable? i.e., store RGB and RGBA values in different structures through unsigned chars, but provide functionality to retrieve float values?
Sorry if this isn't specific enough or what, this is my first question! Let me know what else would be helpful. I can post code for what I have tried so far if you guys would prefer, but hopefully you understand what I'm trying to accomplish.
Upvotes: 2
Views: 2690
Reputation: 146930
You want to make a class for all the possibilities. This is just wrong. One single class can deal with all of them just fine.
First, your GPU will expect 4x [0, 1] floats for colour data, and two, floating [0, 1] is way more precise than [0, 255] RGB colour. That means that it's lossy to go from float 0,1 to char 0, 255. These two facts mean that you realistically have no choice but to use 4x [0, 1] as the implementation. Whether you offer accessors and constructors for [0, 255] in addition is up to you.
Secondly, you can trivially support RGB and RGBA in the same class by simply defaulting A to be 1- this way, anyone who doesn't want to deal with A won't have to.
Upvotes: 2
Reputation: 96109
You only need one class Color (or prefferably Colour)
Internally you can store the value however you like, a 32bit unsigned int RGBA (or BGRA) is common and then just use masks and bit shifts to extract each component.
You can have constructors that take Color(unsigned char red,unsigned char green,unsigned char blue, unsigned char alpha=0xff)
or floats
And similarly getter/setters for each component in each form.
edit: but if you are planning any sort of high performance an image consisting of an array of color objects for each pixels probably isn't the way to go. You might want to look at a set of static functions to set/get each component and a constructor that takes a pixel in your image format's format (ie a BGRA or RGB unsigned int)
Upvotes: 3
Reputation: 66922
This is just coding what the others said because it looks like fun. This is not an answer.
class color {
unsigned char red_, green_, blue_, alpha_;
public:
color()
:red_(0), green_(0), blue_(0), alpha_(0xFF) {}
color(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha=0xFF)
:red_(red), green_(green), blue_(blue), alpha_(alpha) {}
color(float red, float green, float blue, float alpha=1.0)
:red_(red*255.0+.5), green_(green*255.0+.5), blue_(blue*255.0+.5), alpha_(alpha*255.0+.5) {}
color(const color& rhs)
:red_(rhs.red_), green_(rhs.green_), blue_(rhs.blue_), alpha_(rhs.alpha_) {}
//operator= and dtor automatically generated
unsigned char& c_red() {return red_;}
unsigned char& c_green() {return green_;}
unsigned char& c_blue() {return blue_;}
unsigned char& c_alpha() {return alpha_;}
const unsigned char& c_red() const {return red_;}
const unsigned char& c_green() const {return green_;}
const unsigned char& c_blue() const {return blue_;}
const unsigned char& c_alpha() const {return alpha_;}
void set_f_red(float val) {red_=val*255.0+.5;}
void set_f_green(float val) {green_=val*255.0+.5;}
void set_f_blue(float val) {blue_=val*255.0+.5;}
void set_f_alpha(float val) {alpha_=val*255.0+.5;}
float get_f_red() const {return red_/255.0;}
float get_f_green() const {return green_/255.0;}
float get_f_blue() const {return blue_/255.0;}
float get_f_alpha() const {return alpha_/255.0;}
unsigned int rgba() {return (red_<<24)|(green_<<16)|(blue_<<8)|(alpha_<<0);}
};
Upvotes: 1
Reputation: 112366
Xeo's point about over-engineering is a good one, and you might want to read my answer on avoiding over-engineering. But the whole point of OO is to conceal complexity, not encourage it.
Start with a Color class with some efficient internal representation, and overload ctors for the data types you want as arguments. until and unless you have some need to expose the implementation, don't bother with all the subclasses. Take the simplest approach that could possibly work.
Upvotes: 1