mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-28 18:06:09 +02:00
250 lines
6.5 KiB
Lua
250 lines
6.5 KiB
Lua
|
|
local Palette = {}
|
|
|
|
local pack = nil
|
|
|
|
function Palette.packs()
|
|
if pack == nil then
|
|
local ok, data = pcall(require, "data.gb_palettes")
|
|
pack = ok and data or false
|
|
end
|
|
return pack or {}
|
|
end
|
|
|
|
|
|
local MONO_STEPS = { 1.00, 0.72, 0.38, 0.12 }
|
|
|
|
Palette.MONO_COLOURS = {
|
|
{ "GREEN", 0x9b, 0xbc, 0x0f }, -- DMG
|
|
{ "LIME", 0xa8, 0xd8, 0x38 },
|
|
{ "OLIVE", 0xa6, 0xac, 0x84 }, -- Pocket
|
|
{ "AMBER", 0xff, 0xb0, 0x00 }, -- plasma terminal
|
|
{ "ORANGE", 0xff, 0x80, 0x30 },
|
|
{ "RED", 0xff, 0x50, 0x50 },
|
|
{ "ROSE", 0xff, 0x90, 0xb0 },
|
|
{ "MAGENTA", 0xf0, 0x60, 0xd0 },
|
|
{ "PURPLE", 0xb0, 0x70, 0xf0 },
|
|
{ "INDIGO", 0x80, 0x80, 0xf0 },
|
|
{ "BLUE", 0x50, 0xa0, 0xff },
|
|
{ "CYAN", 0x40, 0xe0, 0xe0 }, -- VFD
|
|
{ "TEAL", 0x40, 0xc0, 0xa0 },
|
|
{ "MINT", 0x90, 0xe0, 0xb0 },
|
|
{ "SAND", 0xe0, 0xc8, 0x90 },
|
|
{ "BROWN", 0xc0, 0x90, 0x50 },
|
|
{ "WHITE", 0xff, 0xff, 0xff }, -- plain DMG greys
|
|
{ "SILVER", 0xd0, 0xd8, 0xe0 },
|
|
}
|
|
|
|
local function monoRamp(r, g, b)
|
|
local out = {}
|
|
for i = 1, 4 do
|
|
local f = MONO_STEPS[i]
|
|
out[i] = { math.floor(r * f + 0.5),
|
|
math.floor(g * f + 0.5),
|
|
math.floor(b * f + 0.5) }
|
|
end
|
|
return out
|
|
end
|
|
|
|
Palette.monoRamp = monoRamp -- named for the suite
|
|
|
|
|
|
function Palette.packId(group, name)
|
|
return "p:" .. group .. "/" .. name
|
|
end
|
|
|
|
function Palette.monoId(r, g, b)
|
|
return ("m:%02x%02x%02x"):format(r, g, b)
|
|
end
|
|
|
|
local function unhex(text)
|
|
if type(text) ~= "string" or #text ~= 24 then return nil end
|
|
local out = {}
|
|
for i = 1, 4 do
|
|
local at = (i - 1) * 6
|
|
local r = tonumber(text:sub(at + 1, at + 2), 16)
|
|
local g = tonumber(text:sub(at + 3, at + 4), 16)
|
|
local b = tonumber(text:sub(at + 5, at + 6), 16)
|
|
if not (r and g and b) then return nil end
|
|
out[i] = { r, g, b }
|
|
end
|
|
return out
|
|
end
|
|
|
|
Palette.unhex = unhex -- named for the suite
|
|
|
|
local RAMP_INDEX = 2
|
|
|
|
local GREY_CHROMA = 12
|
|
local HUE_ARC = 50
|
|
|
|
Palette.GREY_CHROMA = GREY_CHROMA -- named for the suite
|
|
Palette.HUE_ARC = HUE_ARC
|
|
|
|
local function hueChroma(c)
|
|
local r, g, b = c[1] or 0, c[2] or 0, c[3] or 0
|
|
local mx = math.max(r, g, b)
|
|
local mn = math.min(r, g, b)
|
|
local chroma = mx - mn
|
|
if chroma <= 0 then return 0, 0 end
|
|
local h
|
|
if mx == r then h = ((g - b) / chroma) % 6
|
|
elseif mx == g then h = (b - r) / chroma + 2
|
|
else h = (r - g) / chroma + 4 end
|
|
return h * 60, chroma
|
|
end
|
|
|
|
Palette.hueChroma = hueChroma -- named for the suite
|
|
|
|
local function hueArc(hues)
|
|
local best = 360
|
|
for i = 1, #hues do
|
|
local widest = 0
|
|
for j = 1, #hues do
|
|
local d = (hues[j] - hues[i]) % 360
|
|
if d > widest then widest = d end
|
|
end
|
|
if widest < best then best = widest end
|
|
end
|
|
return best
|
|
end
|
|
|
|
Palette.hueArc = hueArc -- named for the suite
|
|
|
|
function Palette.categoryOf(ramp)
|
|
if type(ramp) ~= "table" or not ramp[4] then return nil end
|
|
local hues = {}
|
|
for i = 1, 4 do
|
|
local h, chroma = hueChroma(ramp[i])
|
|
if chroma > GREY_CHROMA then hues[#hues + 1] = h end
|
|
end
|
|
if #hues == 0 then return "grey" end
|
|
return hueArc(hues) <= HUE_ARC and "single" or "full"
|
|
end
|
|
|
|
Palette.CATEGORIES = {
|
|
{ key = "full", label = "FULL COLOUR" },
|
|
{ key = "single", label = "SINGLE COLOUR" },
|
|
{ key = "grey", label = "GREYSCALE" },
|
|
}
|
|
|
|
local byId, buckets = nil, nil
|
|
|
|
local function buildIndex()
|
|
byId, buckets = {}, {}
|
|
for _, category in ipairs(Palette.CATEGORIES) do buckets[category.key] = {} end
|
|
for _, p in ipairs(Palette.packs()) do
|
|
for _, entry in ipairs(p.palettes or {}) do
|
|
local name = entry[1]
|
|
if type(name) == "string" then
|
|
local id = Palette.packId(p.name, name)
|
|
byId[id] = entry
|
|
local raw = unhex(entry[RAMP_INDEX] or entry[2])
|
|
local bucket = raw and buckets[Palette.categoryOf(raw) or ""] or nil
|
|
if bucket then
|
|
bucket[#bucket + 1] = { name = name, pack = p.name, id = id }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function ensureIndex()
|
|
if not byId then buildIndex() end
|
|
end
|
|
|
|
function Palette.categories()
|
|
ensureIndex()
|
|
local out = {}
|
|
for i, category in ipairs(Palette.CATEGORIES) do
|
|
out[i] = { key = category.key, name = category.label,
|
|
palettes = buckets[category.key] or {} }
|
|
end
|
|
return out
|
|
end
|
|
|
|
function Palette.category(key)
|
|
ensureIndex()
|
|
if not buckets[key] then return nil end
|
|
for _, category in ipairs(Palette.CATEGORIES) do
|
|
if category.key == key then
|
|
return { key = key, name = category.label, palettes = buckets[key] }
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function Palette.locate(id)
|
|
if type(id) ~= "string" or id == "" then return nil end
|
|
ensureIndex()
|
|
for _, category in ipairs(Palette.CATEGORIES) do
|
|
for i, item in ipairs(buckets[category.key] or {}) do
|
|
if item.id == id then return category.key, i end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function findPack(group, name)
|
|
ensureIndex()
|
|
return byId[Palette.packId(group, name)]
|
|
end
|
|
|
|
Palette.findPack = findPack -- named for the suite
|
|
|
|
function Palette.label(id)
|
|
if type(id) ~= "string" or id == "" then return nil end
|
|
local hex = id:match("^m:(%x+)$")
|
|
if hex then
|
|
local r = tonumber(hex:sub(1, 2), 16)
|
|
local g = tonumber(hex:sub(3, 4), 16)
|
|
local b = tonumber(hex:sub(5, 6), 16)
|
|
for _, c in ipairs(Palette.MONO_COLOURS) do
|
|
if c[2] == r and c[3] == g and c[4] == b then return c[1] end
|
|
end
|
|
return "MONO"
|
|
end
|
|
local name = id:match("^p:.*/([^/]+)$")
|
|
return name or id
|
|
end
|
|
|
|
local cacheId, cacheRamp = nil, nil
|
|
|
|
function Palette.ramp(id)
|
|
if type(id) ~= "string" or id == "" then return nil end
|
|
if id == cacheId then return cacheRamp end
|
|
|
|
local out = nil
|
|
local hex = id:match("^m:(%x+)$")
|
|
if hex and #hex == 6 then
|
|
out = monoRamp(tonumber(hex:sub(1, 2), 16) or 0,
|
|
tonumber(hex:sub(3, 4), 16) or 0,
|
|
tonumber(hex:sub(5, 6), 16) or 0)
|
|
else
|
|
local group, name = id:match("^p:(.*)/([^/]+)$")
|
|
if group then
|
|
local entry = findPack(group, name)
|
|
local ramp = entry and unhex(entry[RAMP_INDEX] or entry[2])
|
|
if ramp then
|
|
out = { ramp[4], ramp[3], ramp[2], ramp[1] }
|
|
end
|
|
end
|
|
end
|
|
|
|
cacheId, cacheRamp = id, out
|
|
return out
|
|
end
|
|
|
|
function Palette.invalidate()
|
|
cacheId, cacheRamp = nil, nil
|
|
byId, buckets = nil, nil
|
|
end
|
|
|
|
function Palette.swatch(id)
|
|
local ramp = Palette.ramp(id)
|
|
if not ramp then return nil end
|
|
return { ramp[4], ramp[3], ramp[2], ramp[1] }
|
|
end
|
|
|
|
return Palette
|