1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-23 12:50:04 +08:00

feat(cpicker): add linear and lab color space

This commit is contained in:
Eric Wong 2024-07-21 14:33:31 +08:00
parent b0f1e65b35
commit 8ff37e2a98
10 changed files with 438 additions and 23 deletions

View File

@ -20,6 +20,7 @@ local util = require('cpicker.util')
local enabled_formats = {}
local increase_keys = {}
local reduce_keys = {}
local color_code_regex = {}
local function update_buf_text()
local rst = {}
@ -35,7 +36,7 @@ local function update_buf_text()
end
end
table.insert(rst, '')
local color_code_regex = {}
color_code_regex = {}
for _, format in ipairs(enabled_formats) do
local ok, f = pcall(require, 'cpicker.formats.' .. format)
if ok then
@ -85,11 +86,7 @@ end
-- https://zenn.dev/kawarimidoll/articles/a8ac50a17477bd
local function copy_color()
local from, to = vim
.regex(
[[#[0123456789ABCDEF]\+\|rgb(\d\+,\s\d\+,\s\d\+)\|hsl(\d\+,\s\d\+%,\s\d\+%)\|hsv(\d\+,\s\d\+%,\s\d\+%)\|cmyk(\d\+%,\s\d\+%,\s\d\+%,\s\d\+%)\|hwb(\d\+,\s\d\+%,\s\d\+%)]]
)
:match_str(vim.fn.getline('.'))
local from, to = vim.regex(table.concat(vim.tbl_map(function(t) return t[2] end, color_code_regex), '\\|')):match_str(vim.fn.getline('.'))
if from then
vim.fn.setreg('+', string.sub(vim.fn.getline('.'), from, to + 1))
notify.notify('copied:' .. string.sub(vim.fn.getline('.'), from, to + 1))

View File

@ -27,10 +27,10 @@ function M.buf_text()
local m_bar = util.generate_bar(magenta, '+')
local y_bar = util.generate_bar(yellow, '+')
local k_bar = util.generate_bar(black, '+')
table.insert(rst, 'CMYK: C: ' .. string.format('%4s', math.floor(cyan * 100 + 0.5)) .. ' ' .. c_bar)
table.insert(rst, ' M: ' .. string.format('%4s', math.floor(magenta * 100 + 0.5)) .. ' ' .. m_bar)
table.insert(rst, ' Y: ' .. string.format('%4s', math.floor(yellow * 100 + 0.5)) .. ' ' .. y_bar)
table.insert(rst, ' K: ' .. string.format('%4s', math.floor(black * 100 + 0.5)) .. ' ' .. k_bar)
table.insert(rst, 'CMYK: C: ' .. string.format('%4s', math.floor(cyan * 100 + 0.5)) .. ' ' .. c_bar)
table.insert(rst, ' M: ' .. string.format('%4s', math.floor(magenta * 100 + 0.5)) .. ' ' .. m_bar)
table.insert(rst, ' Y: ' .. string.format('%4s', math.floor(yellow * 100 + 0.5)) .. ' ' .. y_bar)
table.insert(rst, ' K: ' .. string.format('%4s', math.floor(black * 100 + 0.5)) .. ' ' .. k_bar)
return rst
end

View File

@ -25,14 +25,14 @@ function M.buf_text()
local h_bar = util.generate_bar(hue, '+', 360)
local s_bar = util.generate_bar(saturation, '+')
local l_bar = util.generate_bar(lightness, '+')
table.insert(rst, 'HSL: H: ' .. string.format('%4s', math.floor(hue + 0.5)) .. ' ' .. h_bar)
table.insert(rst, 'HSL: H: ' .. string.format('%4s', math.floor(hue + 0.5)) .. ' ' .. h_bar)
table.insert(
rst,
' S: ' .. string.format('%3s', math.floor(saturation * 100 + 0.5)) .. '% ' .. s_bar
' S: ' .. string.format('%3s', math.floor(saturation * 100 + 0.5)) .. '% ' .. s_bar
)
table.insert(
rst,
' L: ' .. string.format('%3s', math.floor(lightness * 100 + 0.5)) .. '% ' .. l_bar
' L: ' .. string.format('%3s', math.floor(lightness * 100 + 0.5)) .. '% ' .. l_bar
)
return rst
end

View File

@ -26,14 +26,14 @@ function M.buf_text()
local h_bar = util.generate_bar(hue, '+', 360)
local s_bar = util.generate_bar(saturation, '+')
local l_bar = util.generate_bar(value, '+')
table.insert(rst, 'HSV: H: ' .. string.format('%4s', math.floor(hue + 0.5)) .. ' ' .. h_bar)
table.insert(rst, 'HSV: H: ' .. string.format('%4s', math.floor(hue + 0.5)) .. ' ' .. h_bar)
table.insert(
rst,
' S: ' .. string.format('%3s', math.floor(saturation * 100 + 0.5)) .. '% ' .. s_bar
' S: ' .. string.format('%3s', math.floor(saturation * 100 + 0.5)) .. '% ' .. s_bar
)
table.insert(
rst,
' V: ' .. string.format('%3s', math.floor(value * 100 + 0.5)) .. '% ' .. l_bar
' V: ' .. string.format('%3s', math.floor(value * 100 + 0.5)) .. '% ' .. l_bar
)
return rst
end

View File

@ -25,14 +25,14 @@ function M.buf_text()
local h_bar = util.generate_bar(hue, '+', 360)
local w_bar = util.generate_bar(whiteness, '+')
local b_bar = util.generate_bar(blackness, '+')
table.insert(rst, 'HWB: H: ' .. string.format('%4s', math.floor(hue + 0.5)) .. ' ' .. h_bar)
table.insert(rst, 'HWB: H: ' .. string.format('%4s', math.floor(hue + 0.5)) .. ' ' .. h_bar)
table.insert(
rst,
' W: ' .. string.format('%3s', math.floor(whiteness * 100 + 0.5)) .. '% ' .. w_bar
' W: ' .. string.format('%3s', math.floor(whiteness * 100 + 0.5)) .. '% ' .. w_bar
)
table.insert(
rst,
' B: ' .. string.format('%3s', math.floor(blackness * 100 + 0.5)) .. '% ' .. b_bar
' B: ' .. string.format('%3s', math.floor(blackness * 100 + 0.5)) .. '% ' .. b_bar
)
return rst
end

View File

@ -0,0 +1,108 @@
--=============================================================================
-- lab.lua
-- Copyright (c) 2019-2024 Wang Shidong & Contributors
-- Author: Wang Shidong < wsdjeg@outlook.com >
-- URL: https://spacevim.org
-- License: GPLv3
--=============================================================================
local M = {}
local color = require('spacevim.api.color')
local util = require('cpicker.util')
local l = 0 -- [0, 360]
local a = 0 -- [0, 100%]
local b = 0 -- [0, 100%]
M.color_code_regex = [[\slab(\d\+,\s-\?\d\+%,\s-\?\d\+%)]]
local function on_change_argv()
return 'lab', { l, a, b }
end
function M.buf_text()
local rst = {}
local h_bar = util.generate_bar(l, '+', 100)
local s_bar = util.generate_bar(a, '+', 250)
local l_bar = util.generate_bar(b, '+', 250)
table.insert(rst, 'Lab: L: ' .. string.format('%3s', math.floor(l + 0.5)) .. ' ' .. h_bar)
table.insert(rst, ' a: ' .. string.format('%3s', math.floor(a + 0.5)) .. '% ' .. s_bar)
table.insert(rst, ' b: ' .. string.format('%3s', math.floor(b + 0.5)) .. '% ' .. l_bar)
return rst
end
function M.color_code()
return
' =========' .. string.format(
' lab(%s, %s%%, %s%%)',
math.floor(l + 0.5),
math.floor(a + 0.5),
math.floor(b + 0.5)
)
end
local function increase_l()
if l <= 99 then
l = l + 1
elseif l < 100 then
l = 100
end
return on_change_argv()
end
local function reduce_l()
if l >= 1 then
l = l - 1
elseif l > 0 then
l = 0
end
return on_change_argv()
end
local function increase_a()
if a <= 99 then
a = a + 1
elseif a < 100 then
a = 100
end
return on_change_argv()
end
local function reduce_a()
if a >= -99 then
a = a - 1
elseif a > -100 then
a = -100
end
return on_change_argv()
end
local function increase_b()
if b <= 99 then
b = b + 1
elseif b < 100 then
b = 100
end
return on_change_argv()
end
local function reduce_b()
if b >= -99 then
b = b - 1
elseif b > -100 then
b = -100
end
return on_change_argv()
end
function M.increase_reduce_functions()
return {
{ increase_l, reduce_l },
{ increase_a, reduce_a },
{ increase_b, reduce_b },
}
end
function M.on_change(f, code)
if f == 'lab' then
l, a, b = unpack(code)
return
end
l, a, b = color[f .. '2lab'](unpack(code))
end
return M

View File

@ -0,0 +1,108 @@
--=============================================================================
-- rgb-linear
-- Copyright (c) 2019-2024 Wang Shidong & Contributors
-- Author: Wang Shidong < wsdjeg@outlook.com >
-- URL: https://spacevim.org
-- License: GPLv3
--=============================================================================
local M = {}
local color = require('spacevim.api.color')
local util = require('cpicker.util')
local red = 0 -- [0, 1]
local green = 0 -- [0, 1]
local blue = 0 -- [0, 1]
M.color_code_regex = [[\s#[0123456789ABCDEF]\+]]
local function on_change_argv()
return 'linear', { red, green, blue }
end
function M.buf_text()
local rst = {}
local r_bar = util.generate_bar(red, '+')
local g_bar = util.generate_bar(green, '+')
local b_bar = util.generate_bar(blue, '+')
table.insert(
rst,
'Linear: R: ' .. string.format('%4s', math.floor(red * 255 + 0.5)) .. ' ' .. r_bar
)
table.insert(
rst,
' G: ' .. string.format('%4s', math.floor(green * 255 + 0.5)) .. ' ' .. g_bar
)
table.insert(
rst,
' B: ' .. string.format('%4s', math.floor(blue * 255 + 0.5)) .. ' ' .. b_bar
)
return rst
end
function M.color_code()
return ' =========' .. ' ' .. color.linear2hex(red, green, blue)
end
local function increase(c)
if c <= 1 - 1 / 255 then
c = c + 1 / 255
elseif c < 1 then
c = 1
end
return c
end
local function reduce(c)
if c >= 1 / 255 then
c = c - 1 / 255
elseif c > 0 then
c = 0
end
return c
end
local function increase_rgb_red()
red = increase(red)
return on_change_argv()
end
local function reduce_rgb_red()
red = reduce(red)
return on_change_argv()
end
local function increase_rgb_green()
green = increase(green)
return on_change_argv()
end
local function reduce_rgb_green()
green = reduce(green)
return on_change_argv()
end
local function increase_rgb_blue()
blue = increase(blue)
return on_change_argv()
end
local function reduce_rgb_blue()
blue = reduce(blue)
return on_change_argv()
end
function M.increase_reduce_functions()
return {
{ increase_rgb_red, reduce_rgb_red },
{ increase_rgb_green, reduce_rgb_green },
{ increase_rgb_blue, reduce_rgb_blue },
}
end
function M.on_change(f, code)
if f == 'linear' then
red, green, blue = unpack(code)
return
end
red, green, blue = color[f .. '2linear'](unpack(code))
end
return M

View File

@ -27,15 +27,15 @@ function M.buf_text()
local b_bar = util.generate_bar(blue, '+')
table.insert(
rst,
'RGB: R: ' .. string.format('%4s', math.floor(red * 255 + 0.5)) .. ' ' .. r_bar
'RGB: R: ' .. string.format('%4s', math.floor(red * 255 + 0.5)) .. ' ' .. r_bar
)
table.insert(
rst,
' G: ' .. string.format('%4s', math.floor(green * 255 + 0.5)) .. ' ' .. g_bar
' G: ' .. string.format('%4s', math.floor(green * 255 + 0.5)) .. ' ' .. g_bar
)
table.insert(
rst,
' B: ' .. string.format('%4s', math.floor(blue * 255 + 0.5)) .. ' ' .. b_bar
' B: ' .. string.format('%4s', math.floor(blue * 255 + 0.5)) .. ' ' .. b_bar
)
return rst
end

View File

@ -8,7 +8,7 @@
if vim.api.nvim_create_user_command then
local function complete()
return { 'rgb', 'hsl', 'hsv', 'cmyk', 'hwb' }
return { 'rgb', 'hsl', 'hsv', 'cmyk', 'hwb', 'linear', 'lab' }
end
vim.api.nvim_create_user_command('Cpicker', function(opt)
require('cpicker').picker(opt.fargs)

View File

@ -8,6 +8,12 @@
local color = {}
-- rgb <-> hsl - hwb
-- rgb <-> cmyk
-- rgb <-> hsv
-- rgb <-> hex
-- rgb <-> linear <-> xyz <-> lab <-> lch
-- 参考: https://blog.csdn.net/Sunshine_in_Moon/article/details/45131285
color.rgb2hsl = function(r, g, b)
@ -268,4 +274,200 @@ color.hwb2hex = function(h, w, b)
return color.rgb2hex(color.hwb2rgb(h, w, b))
end
color.rgb2linear = function(r, g, b)
return unpack(vim.tbl_map(function(x)
if x <= 0.04045 then
return x / 12.92
end
return ((x + 0.055) / 1.055) ^ 2.4
end, { r, g, b }))
end
color.linear2rgb = function(r, g, b)
return unpack(vim.tbl_map(function(x)
if x <= 0.0031308 then
local a = 12.92 * x
if a > 1 then
return 1
elseif a < 0 then
return 0
else
return a
end
else
local a = 1.055 * x ^ (1 / 2.4) - 0.055
if a > 1 then
return 1
elseif a < 0 then
return 0
else
return a
end
end
end, { r, g, b }))
end
color.linear2hsl = function(r, g, b)
return color.rgb2hsl(color.linear2rgb(r, g, b))
end
color.linear2hwb = function(r, g, b)
return color.rgb2hwb(color.linear2rgb(r, g, b))
end
color.linear2cmyk = function(r, g, b)
return color.rgb2cmyk(color.linear2rgb(r, g, b))
end
color.linear2hsv = function(r, g, b)
return color.rgb2hsv(color.linear2rgb(r, g, b))
end
color.linear2hex = function(r, g, b)
return color.rgb2hex(color.linear2rgb(r, g, b))
end
color.linear2lab = function(r, g, b)
return color.xyz2lab(color.linear2xyz(r, g, b))
end
color.hsl2linear = function(h, s, l)
return color.rgb2linear(color.hsl2rgb(h, s, l))
end
color.hwb2linear = function(h, w, b)
return color.rgb2linear(color.hwb2rgb(h, w, b))
end
color.cmyk2linear = function(c, m, y, k)
return color.rgb2linear(color.cmyk2rgb(c, m, y, k))
end
color.hsv2linear = function(h, s, v)
return color.rgb2linear(color.hsv2rgb(h, s, v))
end
--------------------------------------------------------------------------------
-- XYZ color space
--------------------------------------------------------------------------------
local linear2xyz = {
{ 0.41239079926595, 0.35758433938387, 0.18048078840183 },
{ 0.21263900587151, 0.71516867876775, 0.072192315360733 },
{ 0.019330818715591, 0.11919477979462, 0.95053215224966 },
}
local xyz2linear = {
{ 3.240969941904521, -1.537383177570093, -0.498610760293 },
{ -0.96924363628087, 1.87596750150772, 0.041555057407175 },
{ 0.055630079696993, -0.20397695888897, 1.056971514242878 },
}
local function dot(a, b)
assert(#a == #b)
local result = 0
for i = 1, #a do
result = result + a[i] * b[i]
end
return result
end
local function product(m, v)
local row = #m
local result = {}
for i = 1, row do
result[i] = dot(m[i], v)
end
return unpack(result)
end
function color.linear2xyz(r, g, b)
return product(linear2xyz, { r, g, b })
end
function color.xyz2linear(x, y, z)
return product(xyz2linear, { x, y, z })
end
--------------------------------------------------------------------------------
-- Lab color space
--------------------------------------------------------------------------------
-- What Does CIE L*a*b* Stand for?
--
-- The CIE in CIELAB is the abbreviation for the International Commission on
-- Illuminations French name, Commission Internationale de l´Eclairage.
-- The letters L*, a* and b* represent each of the three values the CIELAB color
-- space uses to measure objective color and calculate color differences.
-- L* represents lightness from black to white on a scale of zero to 100,
-- while a* and b* represent chromaticity with no specific numeric limits.
-- Negative a* corresponds with green, positive a* corresponds with red,
-- negative b* corresponds with blue and positive b* corresponds with yellow.
--
-- https://www.hunterlab.com/blog/what-is-cielab-color-space/
-- https://zh.wikipedia.org/wiki/CIELAB%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4
-- https://en.wikipedia.org/wiki/CIELAB_color_space
-- https://developer.mozilla.org/zh-CN/docs/Web/CSS/color_value/lab
function color.xyz2lab(x, y, z)
local Xn, Yn, Zn = 0.9505, 1, 1.089
local function f(t)
if t > (6 / 29) ^ 3 then
return 116 * t ^ (1 / 3) - 16
end
return (29 / 3) ^ 3 * t
end
return f(y / Yn), (500 / 116) * (f(x / Xn) - f(y / Yn)), (200 / 116) * (f(y / Yn) - f(z / Zn))
end
function color.lab2xyz(l, a, b)
local Xn, Yn, Zn = 0.9505, 1, 1.089
local fy = (l + 16) / 116
local fx = fy + (a / 500)
local fz = fy - (b / 200)
local function t(f)
if f > 6 / 29 then
return f ^ 3
end
return (116 * f - 16) * (3 / 29) ^ 3
end
return t(fx) * Xn, t(fy) * Yn, t(fz) * Zn
end
function color.rgb2lab(r, g, b)
return color.xyz2lab(color.linear2xyz(color.rgb2linear(r, g, b)))
end
function color.lab2rgb(l, a, b)
return color.linear2rgb(color.xyz2linear(color.lab2xyz(l, a, b)))
end
function color.lab2hex(l, a, b)
return color.rgb2hex(color.lab2rgb(l, a, b))
end
function color.lab2hsl(l, a, b)
return color.rgb2hsl(color.lab2rgb(l, a, b))
end
function color.lab2hsv(l, a, b)
return color.rgb2hsv(color.lab2rgb(l, a, b))
end
function color.lab2hwb(l, a, b)
return color.rgb2hwb(color.lab2rgb(l, a, b))
end
function color.lab2cmyk(l, a, b)
return color.rgb2cmyk(color.lab2rgb(l, a, b))
end
function color.lab2linear(l, a, b)
return color.xyz2linear(color.lab2xyz(l, a, b))
end
function color.lab2lch(L, a, b)
local H = math.atan2(b, a)
local C = math.sqrt(a ^ 2 + b ^ 2)
H = H / (2 * math.pi) * 360 -- [rad] -> [deg]
H = H % 360
return L, C, H
end
function color.lch2lab(L, C, H)
H = H / 360 * (2 * math.pi) -- [deg] -> [rad]
local a = C * math.cos(H)
local b = C * math.sin(H)
return L, a, b
end
return color