Groups | Blog | Home
all groups > dotnet drawing api > february 2008 >

dotnet drawing api : Color.Equals seems totally useless


Hilton
2/18/2008 12:49:42 PM
[quoted text, click to view]

IMHO, just because something is documented do not make it not a bug. e.g.
if Intel documented that floating point bug in their CPUs, then it is no
longer a bug?

If GetPixel returns Black then it should equal Color.Black, that's a bug
IMHO. If Msft really cared about 'named colors', then have Color.IsNamed
(color) or something similar.

Hilton

Ben Voigt [C++ MVP]
2/18/2008 1:29:17 PM
I guess this must be a well-known issue that I just hit my head on.

Color.Equals doesn't apply the usual equality test.

For example:

using (g = Graphics.FromImage(bmp))
g.FillRectangle(Brushes.Black, new Rectangle(Point.Empty, bmp.Size));

One might expect that for any (x, y) inside the image bounds,

bmp.GetPixel(x, y) == Color.Black

But it's not.

GetPixel returns an unnamed black color, while Color.Black returns a named
black color, and while the value is the same (0xff000000) they compare
unequal!

Anyone know a valid reason for this insanity before I post a bug on Connect?

Ben Voigt [C++ MVP]
2/18/2008 1:52:56 PM
[quoted text, click to view]

Yeah I just didn't look hard enough...

No mention of that here:
http://msdn2.microsoft.com/en-us/library/system.drawing.color.equals(VS.85).aspx

And the second override just takes you to the general object.Equals docs.

But the first override gives the additional information you posted.

Nicholas Paldino [.NET/C# MVP]
2/18/2008 2:37:30 PM
Ben,

From the documentation for the Equals method on the Color structure:

This structure only does comparisons with other Color structures. To compare
colors based solely on their ARGB values, you should use the ToArgb method.
This is because the Equals and Equality members determine equivalency using
more than just the ARGB value of the colors. For example, Black and
FromArgb(0,0,0) are not considered equal, since Black is a named color and
FromArgb(0,0,0) is not.

I'm not saying it is right, but it's not a bug, as it is documented to
work in this way.


--
- Nicholas Paldino [.NET/C# MVP]
- mvp@spam.guard.caspershouse.com

[quoted text, click to view]
Hilton
2/18/2008 4:28:26 PM
[quoted text, click to view]

Many hardware and software products get shipped with known and (perhaps)
documented bugs. These are bugs, doesn't matter when some person decides to
write the documentation about it. OK, let's say that Microsoft release the
next C# compiler and it is designed to calculate "1+1=3" and they document
this behavior. Is that a bug? Of course it is even though it is "working
as designed". I don't think that it makes any difference where in the
development process the error occurs; i.e. in the research, design,
development, documentation, etc. Comparing two colors which are equal and
giving false sure sounds like a bug to me. Does black equal black? No.
Huh?


[quoted text, click to view]

My guess is that it was incorrectly written/changed to fix a bug somewhere
else in the framework. It worked, got checked in, and voila. In
pseudo-code, it should just be something along the lines of:

public bool Equal (Color color)
{
return (this.R == color.R) && (this.G == color.G) && (this.B == color.B);
}

Right?

I ran a test. The following code amazingly produced "False" and "0 0 0 255"
(both):

Color black = Color.FromArgb (0, 0, 0);
Color color = Color.Black;

MessageBox.Show ((black == color).ToString());

MessageBox.Show (string.Format ("{0} {1} {2} {3}", black.R, black.G,
black.B, black.A));
MessageBox.Show (string.Format ("{0} {1} {2} {3}", color.R, color.G,
color.B, color.A));


Hilton

Peter Duniho
2/18/2008 4:52:05 PM
[quoted text, click to view]

You're welcome to speculate as much as you want, of course. But
personally I find your guess lacking.

I find it much more likely that named colors are in fact treated
differently from unnamed colors in .NET/GDI/GDI+ and there's a much better
reason for them not comparing equal when the ARGB is equal than just that
someone forgot.

For example: the concept of named colors is not a lot different from the
older palette-based color systems. For various reasons, it often made
sense to compare two colors by their palette index rather than actually
looking up the actual color values for the color.

In the context of .NET, consider for example the issue of using a named
color in the context of two different color formats (whether varying only
by bits, or in the actual format, such as RGB vs HSV). Wouldn't it be
beneficial for the same named color to be treated as equal in those
contexts even if the way the color is ultimately used is different?

Conversely, what if the .NET implementors wanted the freedom to be able to
change the underlying implementation of the Color struct without affecting
how equality works and also without suffering the performance hit that
might be required to internally convert to some specific format just to do
an equality test?

This is all speculation as well, of course. But Microsoft has fixed
plenty of other bugs in .NET over the years. It's not like the Color
struct is brand new and it begs credulity to assert that this behavior is
simply a known bug that Microsoft has for whatever reason decided to
ignore. I don't see any rational reason to believe that they'd leave this
bug in when they do in fact fix a wide variety of other bugs as they
become known. It seems much more likely that there's some sort of
implementation detail or way that the Color struct is used that dictates
this particular behavior.

It'd be nice if Microsoft would fill us in on those details, but I don't
see that as mandatory. They do document the behavior, and they document
it as part of the design, not something that's a known bug. To assume
that it is in fact just an oversight, to fail to give Microsoft the
benefit of the doubt without justification for doing so, just doesn't make
any sense.

And finally:

[quoted text, click to view]

Given the information presented in this thread, why in the world are you
surprised? Why is it so amazing that the code does exactly what the
documentation says it will do?

Jon Skeet [C# MVP]
2/18/2008 7:44:01 PM
[quoted text, click to view]

Well, I don't know that I'd say it's a real reason, but it's
documented:

<quote>
This structure only does comparisons with other Color structures. To
compare colors based solely on their ARGB values, you should use the
ToArgb method. This is because the Equals and Equality members
determine equivalency using more than just the ARGB value of the
colors. For example, Black and FromArgb(0,0,0) are not considered
equal, since Black is a named color and FromArgb(0,0,0) is not.
</quote>

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Jon Skeet [C# MVP]
2/18/2008 9:00:20 PM
[quoted text, click to view]

If it had been documented before it had been found, I would call it a
flawed design decision rather than a bug in the implementation. To me,
"bug" = "not working as designed".

[quoted text, click to view]

I suspect there's some reason we haven't thought of, to be honest.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Hilton
2/19/2008 12:36:55 AM
[quoted text, click to view]

So if C# returns 3 when I do "1+1" it is not a bug? If it does the wrong
thing, it is a bug. Anyway, we're kinda going around in circles.


[quoted text, click to view]

Ask 1000 C# developers what "new Color (0, 0, 0).Equals (Color.Black)" would
return...

The MS docs say: "Tests whether the specified object is a Color structure
and is equivalent to this Color structure." Note *Equivalent*. Then
elsewhere, it says "equal" not "equivalent" - this is different.

Then the docs say: "This method compares more than the ARGB values of the
Color structures. It also does a comparison of some state flags. If you want
to compare just the ARGB values of two Color structures, use Color1.ToArgb()
== Color2.ToArgb()." Fine, it is documented, completely unintuitive (see
OP), but documented. So what are Color "state flags". If that is not
documented, then the docs are at best incomplete and ambiguous. Does MSFT
define Color state flags anywhere?

Hilton

Jon Skeet [C# MVP]
2/19/2008 12:44:44 AM
[quoted text, click to view]

If it's documented as a bug, that's one thing. If it's documented as
intended behaviour, that's a different matter.

[quoted text, click to view]

No, I wouldn't call that a bug - not in the software. It's a
flaw/problem/error - whatever you want to call it - in the design, but
not in the software itself.

[quoted text, click to view]

The named colour "black" isn't equal to the unnamed colour which has
the same ARGB value. Should it be? Maybe. Maybe there's a good reason.

Either way, I still wouldn't call it a bug.

[quoted text, click to view]

Given the way it seems MS works, I suspect this really isn't the case.

[quoted text, click to view]

I don't see why you're amazed when it's working exactly as it's
documented to.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Peter Duniho
2/19/2008 1:03:58 AM
[quoted text, click to view]

It's not a bug in the software if the software is specifically designed =
to =

do that, no.

[quoted text, click to view]

If the software is specifically designed to do that, then it's not the =

wrong thing for the software to do.

[quoted text, click to view]

Only because you are starting with a prejudiced view of what is a bug.

A Color structure is simply a piece of data. In this case, it happens t=
o =

represent an abstraction of something we know of in the real world. Som=
e =

such pieces of data follow the exact same rules that we follow in the re=
al =

world, but some do not. I submit to you that in this case, this data =

structure does not.

Likewise, in Jon's reply to your "1+1=3D3" example, you are allowing you=
r =

prejudice of what that exact sequence of characters means in the real =

world to affect your interpretation of what's going on in the computer. =
=

While it's helpful for the compiler to be designed so that adding 1 to =

itself results in 2, just as it does in the real world, some insane =

compiler designer could in fact declare that his compiler will result in=
3 =

when that operation is performed.

He could do so for any variety of reasons, including that one or more of=
=

those symbols don't actually mean in his compiler what they mean in the =
=

real world, or simply that he wants to be arbitrary. But regardless, if=
=

he writes the specification for the compiler stipulating that that's =

what's supposed to happen, then if the compiler does exactly that, then =
=

the compiler is bug-free (at least with respect to that feature).

You may rightfully say that the actual design of the compiler is flawed =
=

(or even "buggy"). But the software itself is operating exactly as it w=
as =

designed, and thus is NOT flawed or buggy. It's not a bug for that =

compiler, with that design, to make 3 the result of adding 1 and 1 (or =

whatever operation might actually be represented by the sequence of =

characters "1+1").

[quoted text, click to view]
=

[quoted text, click to view]

What it _would_ return? Only a developer who is familiar with the =

structure and who knows the exact behavior of the Equals() method should=
=

be answering that question, and they will tell you exactly what the =

documentation says.

Change your question to "should return", and I'd agree that you might ge=
t =

a wider variety of answers, including many (perhaps even a majority) who=
=

agree with you on what it _should_ return.

But that wouldn't be relevant with respect to the question of whether th=
e =

structure's method has a bug in it or not. The only thing relevant ther=
e =

is what Microsoft's .NET designers intended the method to do.

[quoted text, click to view]

You can nit-pick the docs all you want, the fact remains that they still=
=

clearly state the behavior of the Equals() method.

[quoted text, click to view]
=

[quoted text, click to view]

They don't need to, since they aren't part of the API. That is, the =

contract that the Color structure exposes to you.

I don't see how mentioning some aspect of the internal implementation of=
=

the Color structure causes the documentation to be ambiguous.

I can more readily accept the characterization of "incomplete", but only=
=

because it's practically impossible to provide documentation that is =

"complete". In something as vast as .NET, there's always going to be =

hidden corners where the documentation does not provide a literally =

complete discussion of the API. But as long as these oversights don't =

affect how you use the API, I consider them to be insignificant.

So, sure...the docs are incomplete, but in this case only in an =

insignificant way. (There are lots of other places that the docs are =

incomplete in a _significant_ way...why is this the issue you have decid=
ed =

to chase down with such tenacity?)

And certainly in this case, you don't need to know what those flags are.=
=

You don't really even need to know that they exist, though it's nice of =
=

Microsoft to mention them so that perhaps you can at least have a little=
=

bit more insight as to why the Color.Equals() method works the way it do=
es.

Steve Gerrard
2/19/2008 1:23:48 AM
[quoted text, click to view]

As one of the 1000, may I ask what x.ToUpper() is when the current culture is
Turkish? :)

Jon Skeet [C# MVP]
2/19/2008 1:28:01 AM
[quoted text, click to view]

The I has an accent on it. Lower-casing has the same effect (so
y.ToLower()==x wouldn't help). I first ran into this in Java, with
exactly "mail" - I was trying to compare headers.

It's a particularly nasty bit of i18n.

Jon Skeet [C# MVP]
2/19/2008 9:04:50 AM
[quoted text, click to view]

If a particular C# implementation were to return 1+1=3, that would be a
bug - because the specification says that it shouldn't.

To make this concrete, I don't like how overload resolution is applied
in certain cases - but it's not a bug in the C# compiler. It's a design
issue with the spec.

[quoted text, click to view]

Opinions don't dictate whether or not something is a bug. The designed
behaviour does. I could find plenty of other examples where most
developers would guess incorrectly at the answer to a question.
(Floating point is an obvious example where intuition is often wrong.)

Another concrete example:

string x = "mail";
string y = "MAIL";
bool b = (x.ToUpper() == y);

What's the value of b? I suspect if you ask 1000 C# developers, almost
all of them would say "true". Very few of them would say "It depends
whether or not the current culture is Turkish" which is the correct
answer.

[quoted text, click to view]

It doesn't have to be - they're defining the name as part of the
equivalence relation, that's all.

[quoted text, click to view]

Not that I've seen - and that's a fine and genuine beef. Report it on
connect.microsoft.com.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Jon Skeet [C# MVP]
2/19/2008 12:04:31 PM
[quoted text, click to view]

Apologies, yes.

[quoted text, click to view]

I'd say it's worse because it's less expected than many other problems.
In my experience, people are more aware that translating something into
another language may well change how much space it will take up than
they are about this issue.

That's partly because (AFAIK) it only occurs in Turkish - whereas many=20
other i18n issues will become visible in many cultures.

--=20
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Michael C
2/19/2008 12:13:24 PM
[quoted text, click to view]

You're talking about 2 very different situations there and flip flopping
between them. The subtle but HUGE difference is that a design fault in known
before the code is written and a bug is discovered afterwards.

[quoted text, click to view]

It sounds like an inconvenience to me.

Michael

Norman Diamond
2/19/2008 8:07:39 PM
[quoted text, click to view]

The İ has a dot on it not an accent, just like i has.

[quoted text, click to view]

Right, y.ToLower() is "maıl".

[quoted text, click to view]

It's no worse than most other i18n.

NTFS has some trouble though. When creating or opening a file on an NTFS
partition, the upcasing table in the NTFS partition is supposed to be used
.... sometimes ... in order to determine if an equivalently named file
already exists. Sometimes I and ı don't compare as equal. Intuitively it
seems to me that it ought to work correctly if a Windows system had its
default system locale set to Turkish at the time of formatting the NTFS
partition, but intuitively it seems to me that it still likely has a bug.
Norman Diamond
2/19/2008 8:14:37 PM
[quoted text, click to view]

I think we agree that in principle it would be a bug in design but not a bug
in coding.

In practice I think it would yield quite a lot of bugs in the software.
Odds are that the C# compiler contains some + operators and depends on their
results to do a translation. Odds are that the I/O libraries contain some +
operators and depend on their results to write files. Fortunately kernel
mode drivers are in C without adding 1 to it.

In a millennium long long ago in a continent far far away, I changed the
addition table in a computer so it would calculate the result of 2 + 2 as
being 5. But my Fortran program didn't output 2 + 2 = 5, it just didn't
run, because the Fortran compiler and library had too much internal code
that depended on + producing correct results. (Demerit points to anyone who
says what computer this was.)
Jon Skeet [C# MVP]
2/20/2008 12:35:28 AM
[quoted text, click to view]

Sorry, my news client is lovely in many ways - but not great at=20
encodings :( The same will happen again with this one, I'm afraid. (It=20
understands various ISO-8859-? encodings, but not UTF-8.)

[quoted text, click to view]

If you start off with the non-ASCII characters? Yes. I venture to claim=20
that doesn't happen as often as starting with the ASCII ones :)

--=20
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Norman Diamond
2/20/2008 9:29:40 AM
Mr. Skeet, for shame: you took a posting which was encoded in UTF-8
displaying its contents correctly, and you followed up in ISO-8859-1
destroying exactly the contents which needed showing.

Anyway, regarding i <-> İ and ı <-> I,

[quoted text, click to view]

the problem becomes visible in every language *except* Turkish, so it should
be easier than most i18n issues ^_^


[quoted text, click to view]

Apologies, yes.

[quoted text, click to view]

I'd say it's worse because it's less expected than many other problems.
In my experience, people are more aware that translating something into
another language may well change how much space it will take up than
they are about this issue.

That's partly because (AFAIK) it only occurs in Turkish - whereas many
other i18n issues will become visible in many cultures.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
Lew
2/20/2008 8:17:25 PM
[quoted text, click to view]

A better example might be a system that produces "1 + 1 == 10". There is
clearly a domain of interpretation for which that is not "buggy" even in
design, though there could also be people who think that it is buggy, due to
preconceived notions of what "1 + 1" or "10" /should/ mean.

--
Hilton
2/21/2008 12:08:19 AM
Jon,

I call something a bug when it does the 'wrong' thing. So when some code
does the 'wrong' thing, I don't care whether the 'wrong' thing was
introduced in the design, specification, or development of the code. Let's
say that Msft defined "List.Add" as removing an element from the list and
"List.Remove" as adding that element to the list. Is that a bug? Yes, of
course it is (IMHO). Is it coded correctly? Yes, but it is still a bug in
a broader sense. That is my point (which I stated this earlier). Equal
means "equal:, not equal if we name some stuff internally, but that might
all change and it is inconsistent and not well documented "equal". Before
Peter jumps up and down; Peter, please find me the clear concise Msft
definition of Color.Equal.

IMHO, Color.Equal is so broken that it is almost as bad as calling a class
SortedList when it is not a list and does not even implement IList, but
that's a whole 'nother topic. :)

And just to be clear, I love C#, am grateful every day to Microsoft for
creating .NET (especially on devices), so I'm not bashing C# or Msft, I'm
just calling it like I see it.

So, going back to the OP, is Color.Equal useless? If so, then it kinda
proves my point - no?

Hilton

Jon Skeet [C# MVP]
2/21/2008 8:20:07 AM
[quoted text, click to view]

Then you're using the term inconsistently with the rest of the
industry. For instance, from http://en.wikipedia.org/wiki/Computer_bug

<quote>
A software bug (or just "bug") is an error, flaw, mistake, failure, or
fault in a computer program that prevents it from behaving as intended
(e.g., producing an incorrect result).
</quote>

By that definition, if the software is working *as intended* it does
not have a bug.

Then from the jargon file:
http://intranet.cs.man.ac.uk/software/jargon/html/B/bug.html

<quote>
An unwanted and unintended property of a program or piece of hardware,
esp. one that causes it to malfunction.
</quote>

The effect of Color.Equals is the intended one, therefore it doesn't
fall under this definition either.

[quoted text, click to view]

It's poor design. It's not an implementation bug.

[quoted text, click to view]

I agree that without hearing any reasons why it should be designed that
way, it certainly looks like it would make more sense to design it a
different way. That's not the same thing as it being a bug in the
commonly accepted use of the term.

[quoted text, click to view]

To be clear, your complaints have nothing to do with C# itself. The
..NET framework library isn't the same as C# the language.

[quoted text, click to view]

It's not useless if you want to know whether two Color instances are
the same including whether or not they're named.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
Norman Diamond
2/21/2008 7:35:40 PM
Some colours are more equal than others.


[quoted text, click to view]
Michael C
2/22/2008 11:47:54 AM
[quoted text, click to view]

Equal can mean all sorts of things. If you compare 2 lists then they will
always return not equal because they are not the same instance, even if the
lists contain the same elements. In the case of the colour structure we have
2 classes which are probably internally something like this

Color1:
int Colour = 0
int NamedColour = 0

Color2:
int Colour = 0
int NamedColour = 100

Remember this is just a wrapper that *represents* a colour. The designers
would have had 3 choices with equals 1) compare instances 2) Compare just
the Colour field 3) Compare the Colour and NamedColour fields. They simply
chose number 3. To me picking option 2 would be a design flaw because the 2
structures are very clearly not equal.

Michael

AddThis Social Bookmark Button