Last active
February 15, 2025 08:08
-
-
Save FredrikSjoberg/cdea97af68c6bdb0a89e3aba57a966ce to your computer and use it in GitHub Desktop.
Color space conversion between RGB and HSV
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://www.cs.rit.edu/~ncs/color/t_convert.html | |
struct RGB { | |
// Percent | |
let r: Float // [0,1] | |
let g: Float // [0,1] | |
let b: Float // [0,1] | |
static func hsv(r: Float, g: Float, b: Float) -> HSV { | |
let min = r < g ? (r < b ? r : b) : (g < b ? g : b) | |
let max = r > g ? (r > b ? r : b) : (g > b ? g : b) | |
let v = max | |
let delta = max - min | |
guard delta > 0.00001 else { return HSV(h: 0, s: 0, v: max) } | |
guard max > 0 else { return HSV(h: -1, s: 0, v: v) } // Undefined, achromatic grey | |
let s = delta / max | |
let hue: (Float, Float) -> Float = { max, delta -> Float in | |
if r == max { return (g-b)/delta } // between yellow & magenta | |
else if g == max { return 2 + (b-r)/delta } // between cyan & yellow | |
else { return 4 + (r-g)/delta } // between magenta & cyan | |
} | |
let h = hue(max, delta) * 60 // In degrees | |
return HSV(h: (h < 0 ? h+360 : h) , s: s, v: v) | |
} | |
static func hsv(rgb: RGB) -> HSV { | |
return hsv(rgb.r, g: rgb.g, b: rgb.b) | |
} | |
var hsv: HSV { | |
return RGB.hsv(self) | |
} | |
} | |
struct RGBA { | |
let a: Float | |
let rgb: RGB | |
init(r: Float, g: Float, b: Float, a: Float) { | |
self.a = a | |
self.rgb = RGB(r: r, g: g, b: b) | |
} | |
} | |
struct HSV { | |
let h: Float // Angle in degrees [0,360] or -1 as Undefined | |
let s: Float // Percent [0,1] | |
let v: Float // Percent [0,1] | |
static func rgb(h: Float, s: Float, v: Float) -> RGB { | |
if s == 0 { return RGB(r: v, g: v, b: v) } // Achromatic grey | |
let angle = (h >= 360 ? 0 : h) | |
let sector = angle / 60 // Sector | |
let i = floor(sector) | |
let f = sector - i // Factorial part of h | |
let p = v * (1 - s) | |
let q = v * (1 - (s * f)) | |
let t = v * (1 - (s * (1 - f))) | |
switch(i) { | |
case 0: | |
return RGB(r: v, g: t, b: p) | |
case 1: | |
return RGB(r: q, g: v, b: p) | |
case 2: | |
return RGB(r: p, g: v, b: t) | |
case 3: | |
return RGB(r: p, g: q, b: v) | |
case 4: | |
return RGB(r: t, g: p, b: v) | |
default: | |
return RGB(r: v, g: p, b: q) | |
} | |
} | |
static func rgb(hsv: HSV) -> RGB { | |
return rgb(hsv.h, s: hsv.s, v: hsv.v) | |
} | |
var rgb: RGB { | |
return HSV.rgb(self) | |
} | |
/// Returns a normalized point with x=h and y=v | |
var point: CGPoint { | |
return CGPoint(x: CGFloat(h/360), y: CGFloat(v)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment