1
votes

I have a color UIColor(displayP3Red: 1, green: 0, blue: 0.8, alpha: 1). I want to create a desaturated version of this — with the same hue and brightness, but less saturation, like half the saturation of the original. How do I do this?

2

2 Answers

0
votes

It should be straightforward given you can compute the Normalised Primary Matrix (NPM) of DCI-P3: the middle row of the NPM represents the Luminance factors. I will illustrate this using Colour, it assumes that you are using linear values:

import numpy as np
import colour

# Computing the sRGB Luminance Equation, you should be familiar with the 
# resulting Luminance factors.
print(colour.RGB_luminance_equation(
    colour.sRGB_COLOURSPACE.primaries, 
    colour.sRGB_COLOURSPACE.whitepoint))
# Y = 0.212639005872(R) + 0.715168678768(G) + 0.0721923153607(B)

# Computing the DCI-P3 Luminance Equation.
print(colour.RGB_luminance_equation(
    colour.DCI_P3_COLOURSPACE.primaries, 
    colour.DCI_P3_COLOURSPACE.whitepoint))
# Y = 0.209491677913(R) + 0.721595254161(G) + 0.0689130679262(B)

# Computing Luminance of given RGB colour, this is assuming it is representing linear values.
DCI_P3_LUMINANCE_FACTORS = np.array([0.209491677913, 0.721595254161, 0.0689130679262])
RGB = np.array([1.0, 0.0, 0.8])
Y = np.dot(RGB, DCI_P3_LUMINANCE_FACTORS)
print(Y)
# 0.264622132254

With Y representing the Luminance of your given colour, if you wanted to desaturate at 50% you could do something like that:

lerp(RGB, [Y, Y, Y], 0.5)

0
votes
extension UIColor {    
  // Calling this with 0.5 as argument returns a color whose saturation is 50% of that of the receiver.
  func desaturatedBy(fraction: CGFloat) -> UIColor {
    var hue: CGFloat = 0
    var saturation: CGFloat = 0
    var brightness: CGFloat = 0
    var alpha: CGFloat = 0
    let success = getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
    assert(success)

    saturation *= fraction

    return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha)
  }
}

This works because the getter returns HSB in the extended sRGB color space, and UIColor's initialiser takes HSB in the extended sRGB color space, too. So we don't need to do any color space conversion.