Lua - xterm 256 色渐变脚本

这让我有些困扰,而且我不确定是否有答案。我知道有一些模块,比如 Love2D 可以实现渐变,我猜它使用 RGB 颜色。然而,我需要找到一些相似的东西使用 xterm 256 色,但我似乎找不到任何渐变映射来辅助处理。

我猜我将不得不创建一个“最接近 RGB 颜色”的颜色,并从中创建一个渐变,将相应的 RBG 匹配到最接近的 xterm 匹配,但说实话,我甚至不知道从哪里开始。我知道 Python 中有一个“将 xterm 转换为 RGB hex”的脚本(位于 这里),但我不知道如何将其转换为 Lua。

最终,我想做的就是能够将文本变成彩虹渐变色。我目前有一个函数来返回 xterm 颜色,但它完全是随机的,而且输出可能有点难以阅读。这是我用的代码。@x 代表转换为 xterm 颜色,后面是一个三位数字码(001 到 255),然后是文本。

function rainbow(text)
    local rtext = ""
    local xcolor = 1
    local sbyte = 1
    for i = 1, #text do
        math.randomseed(os.time() * xcolor * sbyte)
        sbyte = string.byte(i)
        xcolor = math.random(1, 255)
        rtext = rtext .. "@x" .. string.rep("0", 3 - string.len(xcolor)) .. xcolor .. text:sub(i,i)
    end
    return rtext
end

例如,print(rainbow("测试")) 将导致:

@x211T@x069e@x154s@x177t

显然,这不是渐变,也不是我想要的结果。我想要的是否可能实现,还是一个无望的事情?

编辑

我知道 256 中颜色的限制,也知道没有太多的余地。正如评论中指出的那样,会有很多相同颜色的匹配,所以我会得到一串相同颜色的字符串。实际上,这对我没有关系。无论它做出了多少实际的转换,我希望它可以尽可能地模拟渐变。

如果我能够正确地创建颜色组,而不必轮询图表,那就太好了。我可能需要做的是创建一个“兼容颜色方案”表来处理,除非有人有更好的想法。

点赞
用户1847592
用户1847592

定义 nearest_term256_color_index 函数:

local abs, min, max, floor = math.abs, math.min, math.max, math.floor
local levels = {[0] = 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff}

local function index_0_5(value) -- value = color component 0..255
   return floor(max((value - 35) / 40, value / 58))
end

local function nearest_16_231(r, g, b)   -- r, g, b = 0..255
   -- returns color_index_from_16_to_231, appr_r, appr_g, appr_b
   r, g, b = index_0_5(r), index_0_5(g), index_0_5(b)
   return 16 + 36 * r + 6 * g + b, levels[r], levels[g], levels[b]
end

local function nearest_232_255(r, g, b)  -- r, g, b = 0..255
   local gray = (3 * r + 10 * g + b) / 14
   -- this is a rational approximation for well-known formula
   -- gray = 0.2126 * r + 0.7152 * g + 0.0722 * b
   local index = min(23, max(0, floor((gray - 3) / 10)))
   gray = 8 + index * 10
   return 232 + index, gray, gray, gray
end

local function color_distance(r1, g1, b1, r2, g2, b2)
   return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)
end

local function nearest_term256_color_index(r, g, b)   -- r, g, b = 0..255
   local idx1, r1, g1, b1 = nearest_16_231(r, g, b)
   local idx2, r2, g2, b2 = nearest_232_255(r, g, b)
   local dist1 = color_distance(r, g, b, r1, g1, b1)
   local dist2 = color_distance(r, g, b, r2, g2, b2)
   return dist1 < dist2 and idx1 or idx2
end

定义 generate_gradient 函数,该函数将 @x... 插入您的文本:

local unpack, tonumber = table.unpack or unpack, tonumber

local function convert_color_to_table(rrggbb)
   if type(rrggbb) == "string" then
      local r, g, b = rrggbb:match"(%x%x)(%x%x)(%x%x)"
      return {tonumber(r, 16), tonumber(g, 16), tonumber(b, 16)}
   else
      return rrggbb
   end
end

local function round(x)
   return floor(x + 0.5)
end

local function generate_gradient(text, first_color, last_color)
   local r, g, b = unpack(convert_color_to_table(first_color))
   local dr, dg, db = unpack(convert_color_to_table(last_color))
   local char_pattern = "[^\128-\191][\128-\191]*"
   local n = max(1, select(2, text:gsub(char_pattern, "")) - 1)
   dr, dg, db = (dr - r)/n, (dg - g)/n, (db - b)/n
   local result = ""
   for c in text:gmatch(char_pattern) do
      result = result..("@x%03d"):format(nearest_term256_color_index(
         round(r), round(g), round(b)))..c
      r, g, b = r + dr, g + dg, b + db
   end
   return result
end

在终端中测试:

local function print_with_colors(str)
   print(
      str:gsub("@x(%d%d%d)",
         function(color_idx)
            return "\27[38;5;"..color_idx.."m"
         end)
      .."\27[0m"
   )
end

local str = "Gradient"
local blue, red = {0, 0, 255}, "#FF0000"
str = generate_gradient(str, blue, red)  -- gradient from blue to red
print(str)
print_with_colors(str)

rainbow() 不是渐变色,它应该是一系列渐变色的链。

2016-06-27 13:50:23
用户3670853
用户3670853

我用 ANSI 256 色转义代码模式完成了这个。

-- 参考 ANSI 256 色 = https://robotmoon.com/256-colors/ str = '\27[38;5;34mG\27[38;5;35mr\27[38;5;36ma\27[38;5;37md\27[38;5;38mi\27[38;5;39me\27[38;5;50mn\27[38;5;51mt\27'

if f then f.Destroy() end
f=createForm()
pb=createPaintBox(f)
pb.Canvas.Font.Size=52
pb.Align='alClient'

local rect={}
rect.Left=0
rect.Top=0
rect.Right=pb.Width
rect.Bottom=pb.Height

pb.Repaint()
pb.Canvas.Font.Style='fsBold'
pb.Canvas.Font.Name='Trebuchet MS'
pb.Canvas.Font.Color = 0
pb.Canvas.textRect(rect,16,54,'Gradient')
pb.Canvas.textRect(rect,12,50,str)

感谢 @Egor Skriptunoff 的灵感。 enter image description here

2020-09-15 12:09:51