Click here to Skip to main content
15,885,366 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have:

Several color classes, RGB, CMYK etc.
Each has the same 'ToGrey' methods (Desaturate, Decompose etc.)

Initially there was just one ToGrey method in each class which took a parameter to decide which actual method to use. I'm now implementing the strategy pattern in order to avoid having the switch statements in this earlier method.

Therefore I need to have:
A class for each ToGrey implementation.
An associated interface.
A contect class and an instance of that class in each color class.

However, each ToGrey implementation needs to take any one of several different color classes, so the method signature needs an interface rather than a color class and it needs to be the interface that permits type casting between the color types (since some ToGrey methods are RedChannel, BlueChannel etc.) hence asking for the RedChannel grey version of a CMYK or HSL color becomes possible, since I know I can convert the passed object to an RGB type. Either that or every ToGrey implementation needs to be overloaded with every color type.

That's starting to feel messy and hance the design must be wrong (I think).
Any ideas?

Addendum
==========
The next problem with the above thinking is that once I get an instance of a color (but which I must treat as the interface) and convert it to e.g. RGB in order to get the RedChannel then it's easy apart from when I need to convert it back to its original class type, since I have no idea what that was!


Thanks,
M
Posted
Updated 20-Oct-13 10:38am
v2
Comments
Andreas Gieriet 20-Oct-13 16:40pm    
How do you want to call the ToGrey method? Are the various color classes polymorph, i.e. they implement an IColor interface?
E.g.
IColor color = GetSomeColorImplementation(...);
...
... = color.ToGrey();

Cheers
Andi
M-Badger 20-Oct-13 16:48pm    
aCMYK newColor = new aCMYK();
newColor.ToGrey(GreyScaleMethod.RedChannel)

Public Interface IToGrey
Sub ToGrey(ByRef color As IColorModelConvertible)
End Interface

Public Interface IColorModelConvertible
Function ToARGB(Optional ByVal alpha As Integer = 255) As aRGB
Function ToColor(Optional ByVal alpha As Integer = 255) As Color
Function ToAHSB() As aHSB
Function ToAHSL() As aHSL
Function ToACMYK() As aCMYK
Function ToHex(Optional ByVal alpha As Integer = 255) As String
End Interface

Public Class RGBAverage
Implements IToGrey
Public Sub ToGrey(ByRef color As IColorModelConvertible) Implements IToGrey.ToGrey
Dim newARGB As aRGB = color.ToARGB()
newRGB.Green = newRGB.Red()
newRGB.Blue = newRGB.Red()
color = newRGB.To [Help: Don't know original class type!)
End Sub
Andreas Gieriet 20-Oct-13 18:07pm    
You may add the needed SetFromRGB(...) etc. functions to the IColorModelConvertible to allow modify an existing polymorphic instance. Your last statement in RGBAverage::ToGrey(...) method would then be color.SetFromRGB(newRGB).
Cheers
Andi
Sergey Alexandrovich Kryukov 21-Oct-13 0:00am    
I have a couple of somewhat different ideas. First of all, the concept of color does not have to be the same as OP envisioned it.
Please see my answer.
—SA
Andreas Gieriet 21-Oct-13 2:25am    
Hello Sergey,
I fully agree with your assessment. I was about to write a solution for it but deferred that task since it was more than a quick answer...
Cheers
Andi

1 solution

I think, in your speculations on the classes, interfaces and methods you already limited yourself to some unreasonably rigid set of possible type designs. You need to take a wider look.

I can see two approaches.

First one would be this: you have one and only one color class. This and only this class can be used for all drawing needs, such as rendering. This class should encapsulate the pure idea of color and not expose any color components at all. In other words, internal representation of color should be hidden and not directly accessible Instead, you should have separate unrelated classes called RRG, CMYK, HSL, etc. This classes should not be used for rendering, but only for color calculations through color components. They should conduct the idea of "a point in color space". Let's say, they all have a common base class PointInColorSpace, and this base class is abstract.

Now, the class Color should have a constructor taking a parameter of the type PointInColorSpace. Better yet, each of the classes derived from PointInColorSpace should provide their own implementation of the methods: Color ToColor() returning the object of the class Color and static method void FromColor(Color), and a call to the method ToColor should be the only way to produce and instance of the type Color, except for the "default color", which can be expressed in the form of the parameterless constructor Color().

So, where is the place for ToGray in this schema? Only as Color.ToGray. Only one method for only one (internal, hidden) representation of color. For all other color spaces, the gray point will be found by taking the value returned by ToGray and constructing an instance of PointInColorSpace via each concrete implementation of FromColor. If you consider this schema properly you will find that it is operationally complete.

The second approach could be having different color classes derived from the base class Color. This class would be abstract, in contrast to the very different class Color suggested before. The methods like ToGray would be also abstract, overridden in each of the concrete implementations of color. The second approach is naturally less flexible, even though it may mean less work. Besides, it would not reflect a nature of color. You should make distinction between a color and a point in color space.

—SA
 
Share this answer
 
v3
Comments
Andreas Gieriet 21-Oct-13 2:30am    
Hello Sergey,
my 5!
I would go the first approach - that's what I also had in mind - you wrote it in a nice and concise way!
Have a "normalized" Color class. All the different "color-schema" classes like RGB etc. are transformations into a specific space for calculation with a reverse transforamtion after calculation.
Cheers
Andi
Sergey Alexandrovich Kryukov 21-Oct-13 3:04am    
Thank you Andi.
Yes, this is similar to normalization, and also this is the encapsulation of semantics.
—SA
M-Badger 22-Oct-13 16:25pm    
Let's see if I've understood:
The classes, as conceived, don't think at all about actual drawing, they are just objects to manage colors, either color models or color spaces plus the conversions and the transitions between spaces and models (which is often confused). A color space being the manner in which a model becomes something real rather than abstract.

However, I think you are suggesting this:
A class called Color which contains as a member an interface, let's call it IColorModel.
The classes that manage the color models must implement IColorModel, RGB, CMYK etc.
The Color class should have a second member called IColorSpace which allows the model to be interpreted in such a way that it can be rendered correctly.
Color model conversions, e.g. RGB to CMYK, simply change the IColorModel member.
Color space changes, e.g. CIE 1931 XYZ to Adobe RGB change the IColorSpace member and have consequent changes on the IColorModel member, either a complete change or a change in the detail: CIELAB to sRGB would change both, Adobe RGB to sRGB would change the space and the detail of the model.
The ToGray methods, as an example, would call the relavant method of the IColor member which would probably also implement the IColorToGrey interface, both interface members pointing at the same actual object.

Have I understood anything?
Sergey Alexandrovich Kryukov 22-Oct-13 16:43pm    
Almost. In the first approach, I did not suggest that the type Color should be abstracted from the color space (and the type is not abstract). I suggested that the color-space presentation is hidden in this type. Under the hood (in private members, or even internal), it can be whatever you find more convenient, it could be a point on one of those concrete color spaces. I only suggest you make the point in color space only exposed through the concrete color space types, through the methods I mentioned.

Moreover, I completely excluded from consideration of any common interfaces like IColorModel. If you look at what it can be, you will see that members of different types of PointInColorSpace have nothing in common. That's why you won't really need such interface at all.

Did I explain it?

—SA
M-Badger 22-Oct-13 17:02pm    
I think it may help if you assume I'm a rank amateur.
(I wonder if there is some confusion? I am looking at a library rather than an application, does that make a difference?)
Do you mean that it (and it would be only one) would work with a hidden color space, say CIE 1931 XYZ since it is the common reference, and that to obtain a model or a space, whichever is desired, this object would have methods that kick out a new object of the desired type? Presumably it could then construct itself on the basis of being given any color object, model or space - the reverse to the previous sentence and that perhaps the constructor could have an optional parameter of space, the default of which exposes the default space which is not directly accessible otherwise? How would you assign colors the model that it contains without specifying a model type and exposing its members, Red, Green and Blue for example?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900