Overview
Comment: | feat(color): Change color lightening to use HSL Change color lightening, including the readable utility, to use HSL. This is because the earlier implementation was broken and hacky, and using HSL is a way to implement this in a much more natural-feeling way (being closer to percieved lightness), especially for the current uses. Add a color:to_hsl() function to make this easier, as well as a from_hsl utility that is only in color, for now, but maybe should be exposed as an alternate constructor? |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | glowpelt/hsl |
Files: | files | file ages | folders |
SHA3-256: |
0a49ac4849099b7ab705e205893c11f7 |
User & Date: | glowpelt on 2020-08-13 05:11:22 |
Other Links: | branch diff | manifest | tags |
Context
2020-08-14
| ||
06:17 | fix(color): Actually get hsl brightening working Reimplemented based on the algorithms in Computer Graphics: Principles and Practice. Only lightens based on luminosity right now, which makes beautifully saturated, but not pastel colors, so some tweaking might be recommended. Leaf check-in: 794d5b023a user: glowpelt tags: glowpelt/hsl | |
2020-08-13
| ||
05:11 | feat(color): Change color lightening to use HSL Change color lightening, including the readable utility, to use HSL. This is because the earlier implementation was broken and hacky, and using HSL is a way to implement this in a much more natural-feeling way (being closer to percieved lightness), especially for the current uses. Add a color:to_hsl() function to make this easier, as well as a from_hsl utility that is only in color, for now, but maybe should be exposed as an alternate constructor? check-in: 0a49ac4849 user: glowpelt tags: glowpelt/hsl | |
2020-08-11
| ||
21:39 | initial commit check-in: 956134c50b user: lexi tags: trunk | |
Changes
Modified lib/color.lua from [16b55419da] to [1e12e4a44b].
51 51 } end; 52 52 }; 53 53 54 54 construct = function(r,g,b,a) 55 55 local clip = function(v) 56 56 return math.max(0,math.min(255,v)) 57 57 end; 58 + local from_hsl = function(hsl, alpha) 59 + -- convert from a hsl table and alpha value to a color 60 + local weird = function(n) 61 + -- Yeah... this is a really weird function, only named f 62 + local k = math.fmod(n + hsl.hue/(math.pi/6),12) 63 + local a = hsl.saturation * math.min(hsl.luminosity, 1 - hsl.luminosity) 64 + return hsl.luminosity * math.max(-1, math.min(k-3,9-k,1)) 65 + end 66 + return color(clip(weird(0)*255), clip(weird(8)*255), clip(weird(4)*255), alpha) 67 + end; 58 68 local warp = function(f) 59 69 return function(self, ...) 60 70 local n = color(self) 61 71 f(n, ...) 62 72 return n 63 73 end; 64 74 end; ................................................................................ 70 80 fmt = function(self, text) return 71 81 minetest.colorize(self:hex(), text) 72 82 end; 73 83 74 84 luminosity = function(self) return 75 85 (self.red + self.green + self.blue) / 3 76 86 end; 87 + 88 + to_hsl = function(self) 89 + -- https://en.wikipedia.org/wiki/HSL_and_HSV 90 + -- www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/ 91 + -- https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.211.6425 92 + -- https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.413.9004 93 + -- We need the rgb between 0 and 1 94 + local rgb = { r = self.red/255, g = self.green/255, b = self.blue/255 } 95 + -- First, the hue. 96 + -- This version of the calculation can be up to 1.12deg off at the right few colors 97 + -- but is overall very close, easier to implement, and runs much faster 98 + -- TODO: consider memoizing something to do with this all? 99 + local alpha = 0.5 * (2*rgb.r - rgb.g - rgb.b) 100 + local beta = (math.sqrt(3)/2)*(rgb.g - rgb.b) 101 + local hue = math.atan2(beta, alpha) 102 + -- Next the luminosity/lightness. This one's easy enough 103 + local luminosity = 0.5*(math.max(rgb.r, rgb.g, rgb.b) + math.min(rgb.r, rgb.g, rgb.b)) 104 + -- Finally, saturation 105 + local saturation = 0 106 + -- If luminosity isn't essentially 1 or 0 107 + if math.abs(luminosity - 1) < 1e-18 or math.abs(luminosity) < 1e-18 then 108 + -- need the chroma 109 + local chroma = math.max(rgb.r,rgb.g,rgb.b) - math.min(rgb.r,rgb.g,rgb.b) 110 + saturation = chroma / (1 - math.abs(2*luminosity - 1)) 111 + end 112 + return { hue = hue, saturation = saturation, luminosity = luminosity } 113 + end; 77 114 78 115 readable = function(self, target) 79 116 target = target or 200 80 - local lum = self:luminosity() 81 - if lum < target then 82 - local f = 1.0 + (target - lum) / 255 83 - local nc = self:brighten(f * 1.5) 84 - -- i don't know why the *1.5 is necessary. it's 85 - -- an ugly hack to work around broken math, 86 - -- because i'm too much of a mathtard to actually 87 - -- find what's wrong 88 - return nc 89 - else 90 - return self 91 - end 117 + local hsl = self:to_hsl() 118 + hsl.luminosity = target 119 + return from_hsl(hsl, self.alpha) 92 120 end; 93 121 94 122 bg = function(self, text) return 95 123 text .. minetest.get_background_escape_sequence(self:hex()) 96 124 end; 97 125 98 126 fade = warp(function(new, fac) 99 127 new.alpha = math.min(255, (new.alpha or 255) * fac) 100 128 end); 101 129 102 - brighten = warp(function(new, fac) 103 - local lum = new:luminosity() 104 - local newlum = lum * fac 105 - local delta = (newlum - lum) 106 - new.red = clip(new.red + delta) 107 - new.blue = clip(new.blue + delta) 108 - new.green = clip(new.green + delta) 109 - end); 130 + brighten = function(self, fac) 131 + -- Use HSL to brighten 132 + -- To HSL 133 + local hsl = self:to_hsl() 134 + -- Do the calculation, clamp to 0-1 instead of the clamp fn 135 + hsl.luminosity = math.max(math.min(hsl.luminosity * fac, 0), 1) 136 + -- Turn back into RGB color 137 + return from_hsl(hsl, self.alpha) 138 + end; 110 139 111 140 darken = warp(function(new, fac) 141 + -- TODO: is there any point to this being different than brighten? Probably especially not now. 112 142 new.red = clip(new.red - (new.red * fac)) 113 143 new.blue = clip(new.blue - (new.blue * fac)) 114 144 new.green = clip(new.green - (new.green * fac)) 115 145 end); 116 146 } 117 147 if g == nil then 118 148 if type(r) == 'string' then