• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excessive / cpml / 1

23 Jun 2021 11:35PM UTC coverage: 53.574% (+9.4%) from 44.185%
1

push

github

web-flow
Merge pull request #62 from idbrii/actions-busted

Run tests on gh actions

4452 of 8310 relevant lines covered (53.57%)

91.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.71
/modules/color.lua
1
--- Color utilities
2
-- @module color
3

4
local modules  = (...):gsub('%.[^%.]+$', '') .. "."
2✔
5
local utils    = require(modules .. "utils")
2✔
6
local color    = {}
2✔
7
local color_mt = {}
2✔
8

9
local function new(r, g, b, a)
10
        local c = { r, g, b, a }
×
11
        c._c = c
×
12
        return setmetatable(c, color)
×
13
end
14

15
-- HSV utilities (adapted from http://www.cs.rit.edu/~ncs/color/t_convert.html)
16
-- hsv_to_color(hsv)
17
-- Converts a set of HSV values to a color. hsv is a table.
18
-- See also: hsv(h, s, v)
19
local function hsv_to_color(hsv)
20
        local i
21
        local f, q, p, t
22
        local h, s, v
23
        local a = hsv[4] or 255
×
24
        s = hsv[2]
×
25
        v = hsv[3]
×
26

27
        if s == 0 then
×
28
                return new(v, v, v, a)
×
29
        end
30

31
        h = hsv[1] / 60
×
32

33
        i = math.floor(h)
×
34
        f = h - i
×
35
        p = v * (1-s)
×
36
        q = v * (1-s*f)
×
37
        t = v * (1-s*(1-f))
×
38

39
        if     i == 0 then return new(v, t, p, a)
×
40
        elseif i == 1 then return new(q, v, p, a)
×
41
        elseif i == 2 then return new(p, v, t, a)
×
42
        elseif i == 3 then return new(p, q, v, a)
×
43
        elseif i == 4 then return new(t, p, v, a)
×
44
        else               return new(v, p, q, a)
×
45
        end
46
end
47

48
-- color_to_hsv(c)
49
-- Takes in a normal color and returns a table with the HSV values.
50
local function color_to_hsv(c)
51
        local r = c[1]
×
52
        local g = c[2]
×
53
        local b = c[3]
×
54
        local a = c[4] or 255
×
55
        local h, s, v
56

57
        local min = math.min(r, g, b)
×
58
        local max = math.max(r, g, b)
×
59
        v = max
×
60

61
        local delta = max - min
×
62

63
        -- black, nothing else is really possible here.
64
        if min == 0 and max == 0 then
×
65
                return { 0, 0, 0, a }
×
66
        end
67

68
        if max ~= 0 then
×
69
                s = delta / max
×
70
        else
71
                -- r = g = b = 0 s = 0, v is undefined
72
                s = 0
×
73
                h = -1
×
74
                return { h, s, v, 255 }
×
75
        end
76

77
        if r == max then
×
78
                h = ( g - b ) / delta     -- yellow/magenta
×
79
        elseif g == max then
×
80
                h = 2 + ( b - r ) / delta -- cyan/yellow
×
81
        else
82
                h = 4 + ( r - g ) / delta -- magenta/cyan
×
83
        end
84

85
        h = h * 60 -- degrees
×
86

87
        if h < 0 then
×
88
                h = h + 360
×
89
        end
90

91
        return { h, s, v, a }
×
92
end
93

94
--- The public constructor.
95
-- @param x Can be of three types: </br>
96
-- number red component 0-255
97
-- table {r, g, b, a}
98
-- nil for {0,0,0,0}
99
-- @tparam number g Green component 0-255
100
-- @tparam number b Blue component 0-255
101
-- @tparam number a Alpha component 0-255
102
-- @treturn color out
103
function color.new(r, g, b, a)
2✔
104
        -- number, number, number, number
105
        if r and g and b and a then
×
106
                assert(type(r) == "number", "new: Wrong argument type for r (<number> expected)")
×
107
                assert(type(g) == "number", "new: Wrong argument type for g (<number> expected)")
×
108
                assert(type(b) == "number", "new: Wrong argument type for b (<number> expected)")
×
109
                assert(type(a) == "number", "new: Wrong argument type for a (<number> expected)")
×
110

111
                return new(r, g, b, a)
×
112

113
        -- {r, g, b, a}
114
        elseif type(r) == "table" then
×
115
                local rr, gg, bb, aa = r[1], r[2], r[3], r[4]
×
116
                assert(type(rr) == "number", "new: Wrong argument type for r (<number> expected)")
×
117
                assert(type(gg) == "number", "new: Wrong argument type for g (<number> expected)")
×
118
                assert(type(bb) == "number", "new: Wrong argument type for b (<number> expected)")
×
119
                assert(type(aa) == "number", "new: Wrong argument type for a (<number> expected)")
×
120

121
                return new(rr, gg, bb, aa)
×
122
        end
123

124
        return new(0, 0, 0, 0)
×
125
end
126

127
--- Convert hue,saturation,value table to color object.
128
-- @tparam table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-255}
129
-- @treturn color out
130
color.hsv_to_color_table = hsv_to_color
2✔
131

132
--- Convert color to hue,saturation,value table
133
-- @tparam color in
134
-- @treturn table hsva {hue 0-359, saturation 0-1, value 0-1, alpha 0-255}
135
color.color_to_hsv_table = color_to_hsv
2✔
136

137
--- Convert hue,saturation,value to color object.
138
-- @tparam number h hue 0-359
139
-- @tparam number s saturation 0-1
140
-- @tparam number v value 0-1
141
-- @treturn color out
142
function color.from_hsv(h, s, v)
2✔
143
        return hsv_to_color { h, s, v }
×
144
end
145

146
--- Convert hue,saturation,value to color object.
147
-- @tparam number h hue 0-359
148
-- @tparam number s saturation 0-1
149
-- @tparam number v value 0-1
150
-- @tparam number a alpha 0-255
151
-- @treturn color out
152
function color.from_hsva(h, s, v, a)
2✔
153
        return hsv_to_color { h, s, v, a }
×
154
end
155

156
--- Invert a color.
157
-- @tparam color to invert
158
-- @treturn color out
159
function color.invert(c)
2✔
160
        return new(255 - c[1], 255 - c[2], 255 - c[3], c[4])
×
161
end
162

163
--- Lighten a color by a component-wise fixed amount (alpha unchanged)
164
-- @tparam color to lighten
165
-- @tparam number amount to increase each component by, 0-255 scale
166
-- @treturn color out
167
function color.lighten(c, v)
2✔
168
        return new(
×
169
                utils.clamp(c[1] + v * 255, 0, 255),
×
170
                utils.clamp(c[2] + v * 255, 0, 255),
×
171
                utils.clamp(c[3] + v * 255, 0, 255),
×
172
                c[4]
173
        )
174
end
175

176
function color.lerp(a, b, s)
2✔
177
        return a + s * (b - a)
×
178
end
179

180
--- Darken a color by a component-wise fixed amount (alpha unchanged)
181
-- @tparam color to darken
182
-- @tparam number amount to decrease each component by, 0-255 scale
183
-- @treturn color out
184
function color.darken(c, v)
2✔
185
        return new(
×
186
                utils.clamp(c[1] - v * 255, 0, 255),
×
187
                utils.clamp(c[2] - v * 255, 0, 255),
×
188
                utils.clamp(c[3] - v * 255, 0, 255),
×
189
                c[4]
190
        )
191
end
192

193
--- Multiply a color's components by a value (alpha unchanged)
194
-- @tparam color to multiply
195
-- @tparam number to multiply each component by
196
-- @treturn color out
197
function color.multiply(c, v)
2✔
198
        local t = color.new()
×
199
        for i = 1, 3 do
×
200
                t[i] = c[i] * v
×
201
        end
202

203
        t[4] = c[4]
×
204
        return t
×
205
end
206

207
-- directly set alpha channel
208
-- @tparam color to alter
209
-- @tparam number new alpha 0-255
210
-- @treturn color out
211
function color.alpha(c, v)
2✔
212
        local t = color.new()
×
213
        for i = 1, 3 do
×
214
                t[i] = c[i]
×
215
        end
216

217
        t[4] = v * 255
×
218
        return t
×
219
end
220

221
--- Multiply a color's alpha by a value
222
-- @tparam color to multiply
223
-- @tparam number to multiply alpha by
224
-- @treturn color out
225
function color.opacity(c, v)
2✔
226
        local t = color.new()
×
227
        for i = 1, 3 do
×
228
                t[i] = c[i]
×
229
        end
230

231
        t[4] = c[4] * v
×
232
        return t
×
233
end
234

235
--- Set a color's hue (saturation, value, alpha unchanged)
236
-- @tparam color to alter
237
-- @tparam hue to set 0-359
238
-- @treturn color out
239
function color.hue(col, hue)
2✔
240
        local c = color_to_hsv(col)
×
241
        c[1] = (hue + 360) % 360
×
242
        return hsv_to_color(c)
×
243
end
244

245
--- Set a color's saturation (hue, value, alpha unchanged)
246
-- @tparam color to alter
247
-- @tparam hue to set 0-359
248
-- @treturn color out
249
function color.saturation(col, percent)
2✔
250
        local c = color_to_hsv(col)
×
251
        c[2] = utils.clamp(percent, 0, 1)
×
252
        return hsv_to_color(c)
×
253
end
254

255
--- Set a color's value (saturation, hue, alpha unchanged)
256
-- @tparam color to alter
257
-- @tparam hue to set 0-359
258
-- @treturn color out
259
function color.value(col, percent)
2✔
260
        local c = color_to_hsv(col)
×
261
        c[3] = utils.clamp(percent, 0, 1)
×
262
        return hsv_to_color(c)
×
263
end
264

265
-- http://en.wikipedia.org/wiki/SRGB#The_reverse_transformation
266
function color.gamma_to_linear(r, g, b, a)
2✔
267
        local function convert(c)
268
                if c > 1.0 then
×
269
                        return 1.0
×
270
                elseif c < 0.0 then
×
271
                        return 0.0
×
272
                elseif c <= 0.04045 then
×
273
                        return c / 12.92
×
274
                else
275
                        return math.pow((c + 0.055) / 1.055, 2.4)
×
276
                end
277
        end
278

279
        if type(r) == "table" then
×
280
                local c = {}
×
281
                for i = 1, 3 do
×
282
                        c[i] = convert(r[i] / 255) * 255
×
283
                end
284

285
                c[4] = convert(r[4] / 255) * 255
×
286
                return c
×
287
        else
288
                return convert(r / 255) * 255, convert(g / 255) * 255, convert(b / 255) * 255, a or 255
×
289
        end
290
end
291

292
-- http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29
293
function color.linear_to_gamma(r, g, b, a)
2✔
294
        local function convert(c)
295
                if c > 1.0 then
×
296
                        return 1.0
×
297
                elseif c < 0.0 then
×
298
                        return 0.0
×
299
                elseif c < 0.0031308 then
×
300
                        return c * 12.92
×
301
                else
302
                        return 1.055 * math.pow(c, 0.41666) - 0.055
×
303
                end
304
        end
305

306
        if type(r) == "table" then
×
307
                local c = {}
×
308
                for i = 1, 3 do
×
309
                        c[i] = convert(r[i] / 255) * 255
×
310
                end
311

312
                c[4] = convert(r[4] / 255) * 255
×
313
                return c
×
314
        else
315
                return convert(r / 255) * 255, convert(g / 255) * 255, convert(b / 255) * 255, a or 255
×
316
        end
317
end
318

319
--- Check if color is valid
320
-- @tparam color to test
321
-- @treturn boolean is color
322
function color.is_color(a)
2✔
323
        if type(a) ~= "table" then
×
324
                return false
×
325
        end
326

327
        for i = 1, 4 do
×
328
                if type(a[i]) ~= "number" then
×
329
                        return false
×
330
                end
331
        end
332

333
        return true
×
334
end
335

336
--- Return a formatted string.
337
-- @tparam color a color to be turned into a string
338
-- @treturn string formatted
339
function color.to_string(a)
2✔
340
        return string.format("[ %3.0f, %3.0f, %3.0f, %3.0f ]", a[1], a[2], a[3], a[4])
×
341
end
342

343
function color_mt.__index(t, k)
2✔
344
        if type(t) == "cdata" then
×
345
                if type(k) == "number" then
×
346
                        return t._c[k-1]
×
347
                end
348
        end
349

350
        return rawget(color, k)
×
351
end
352

353
function color_mt.__newindex(t, k, v)
2✔
354
        if type(t) == "cdata" then
×
355
                if type(k) == "number" then
×
356
                        t._c[k-1] = v
×
357
                end
358
        end
359
end
360

361
color_mt.__tostring = color.to_string
2✔
362

363
function color_mt.__call(_, r, g, b, a)
2✔
364
        return color.new(r, g, b, a)
×
365
end
366

367
function color_mt.__add(a, b)
2✔
368
        return new(a[1] + b[1], a[2] + b[2], a[3] + b[3], a[4] + b[4])
×
369
end
370

371
function color_mt.__sub(a, b)
2✔
372
        return new(a[1] - b[1], a[2] - b[2], a[3] - b[3], a[4] - b[4])
×
373
end
374

375
function color_mt.__mul(a, b)
2✔
376
        if type(a) == "number" then
×
377
                return new(a * b[1], a * b[2], a * b[3], a * b[4])
×
378
        elseif type(b) == "number" then
×
379
                return new(b * a[1], b * a[2], b * a[3], b * a[4])
×
380
        else
381
                return new(a[1] * b[1], a[2] * b[2], a[3] * b[3], a[4] * b[4])
×
382
        end
383
end
384

385
return setmetatable({}, color_mt)
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc