ID:1623178
 
(See the best response by Ter13.)
In our codebase, we have a system where players can choose their body colour and it get's applied to their mob's base sprite using ICON_ADD.

ICON_ADD is terrible since it adds a heavy white bias to the mob's appearance, because it basically takes the two RGB values and adds them. The person who implemented the system ended up darkening all the base sprites to compensate, but this is unsatisfactory as it reduces the amount of dynamic range available for showing details on the sprite.

Someone with more knowledge in graphics suggested we use ICON_MULTIPLY instead, but I'm a little unsure about it because I don't understand how it works.

How is it possible to multiply two RGB values and remain within the 8-bit channel range? Does it rescale the values after multiplication? How does it do so? Square root? Linear rescaling?
Best response
The way multiply works, is that it interprets the Red, Green, and Blue color channels as decimals between 0 and 1.

So 255 would be 1, and 0 would be 0.

The math works out to be:

(source / 255) * (target / 255) * 255


or shorthand:

source * target / 255
Ah, I see it would introduce a black bias then, because of the way multiplying numbers less than 1 always tends to zero.

Also if you blend two identical colours this way, you get a darker colour.

Still, it seems better than adding RGB colours by a long shot.

Do you know if there is a way to implement a custom colour blend mode? I'm thinking taking the square root of the product of the two colours would be much better.
MapColors would be your best bet for better control, but as with any function LummoxJR has introduced to the software suite, it's as powerful as it is difficult to understand.
Just to make sure I understand MapColors right, it replaces each channel in the new colour with a weighted sum of the channels of the old colour?

So r_new = rr*r_old + rg*g_old + rb*b_old + ra*a_old + r0, etc?

Or to put it another way,

if colour_new and colour_old are vectors (r,g,b,a), then:

colour_new = M*colour_old + (r0, b0, g0, a0)

where M is the matrix:
[ rr, rg, rb, ra ]
[ gr, gg, gb, ga ]
[ br, bg, bb, ba ]
[ ar, ag, ab, aa ]


Do I understand this correctly?

I'm not so sure if this will work for my purposes, as what I want to do is make (r,g,b,a) a function of the old colour and the colour being blended in.

Unless there is a way I can pass in a function to MapColors and have it use that as the elements of M.
To be honest, MapColors is beyond even me.
Alright. I just wish there was a way to blend colours that resulted in each channel having the square root of the product of that channel from two colours.

That way I wouldn't have to go and modify all of our icons to support colour blending. It also seems like it would be more useful than the current ICON_MULTIPLY in general, as it has the nice property of keeping the colour unchanged when two identical colours are blended together.
LummoxJR wrote a library with some uses of MapColors(). The ColorTone() proc in particular may do about what you want:

http://www.byond.com/developer/LummoxJR/IconProcs
http://www.byond.com/forum/?post=51608
Thanks, I'll try it out.

Edit:

ColorTone() appears to greyscale an image before applying the colour, which makes the icon monochrome. Not really what I want, but the source code is available so perhaps it can give me some ideas.