DEV Community

Discussion on: Understanding the Color type in Go

Collapse
 
linutic profile image
Gene Olson

Color arithmetic is a good subject to cover, and I appreciate the way you handled it here. However I wonder about your use of floating point arithmetic. Looking in the color module
now dated April 2026, I don't see any floating point there. It's all just integer arithmetic.

If you are still interested in this topic, I suggest you address coversion of color from one format to another. It took me nearly a day to decode the methods used in the color module. Perhaps you could explain the function calls needed to do the conversion. Here is an example I put together so I can remember how to do it.

`red := color.NRGBA{R: 0xff, G: 0x33, B: 0x33, A: 0xff}
green := color.NRGBA{R: 0, G: 180, B: 0, A: 255}
blue := color.NRGBA{R: 0, G: 0, B: 180, A: 255}

// color.NRGBAModel is a variable that contains an interface of type
// Model. The model interface supports a function Convert to convert
// any color.Color into any other color.Color.  In this case NRGBAModel
// contains the unexported color.nrgbaModel() function which converts
// any color.Color to a color.NRGBA but then returns that value as
// a color.Color. As the function pointer has been converted to
// a Model, the only way to use use this function to convert a
// color to a color.NRGBA is to call Convert() and then do a
// color.NRGBA type assertion.  Seems like it would have been a lot
// easier to have an exported function in the color module. The syntax
// below is pretty obscure, and I found no examples in the color module.

myBlack := color.NRGBAModel.Convert(color.Black).(color.NRGBA)
myWhite := color.NRGBAModel.Convert(color.White).(color.NRGBA)

colors := []color.NRGBA{red, green, blue, myBlack, myWhite}`
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mokiat profile image
Momchil Atanasov

Hi! Thanks for looking into this and for the feedback.

It's been a while since I wrote this article. What I meant by float in the article is that pre-multiplied doesn't mean "just multiply the two values" but instead it implies that the alpha is treated as a fraction.

My initial code does use float to do exactly that in order to show the most straight-forward and easy implementation, but as can be seen in the Official implementation section, I do mention that the official implementation does NOT use float. Here is a quote from my article:

There is clearly a major difference between the two implementations. How is it that the official implementation avoids the usage of float32 values and what are all the bit-wise operations doing?

The subsequent section (Iterative optimization) covers exaclty that - going from the floating point code (which is easy to comprehend) and getting to the official code (which uses bit-shifting and is more optimal).

As for the color.Model interface, you are right, I did not cover it in this article and in hindsight maybe I should have. So thanks for adding that in your comment - readers will find it useful.

It provides an easy way to convert from one color to the next, though one needs to be careful, since in some cases it could be sub-optimal - from what I recall, it uses multiple interface indirections and requires that source colors be converted to the Color format and afterwards be convcerted to the target Model format. In some cases there might be a more trivial direct conversion (e.g. color.NRGBA to hdrcolor.RGB).