Hi all,
I know I’m late to the party, but I wanted to chip in. Color is not my specialty, so if anyone knows more about it, correct away. I looked at this Wikipedia section on HSL and HSV. Given this image, the default Aseprite gray conversion is
which looks like luminance per rec. 709 with a gamma adjustment of 1.0.
Then I wrote this script as a test against the two controls (Aseprite default and Wikipedia) using this table as reference.
The example script below implements 601 and 709.
local dlg = Dialog { title = "Luminance Gray" }
dlg:combobox {
id = "standard",
label = "Standard:",
option = "REC_709",
options = { "REC_601", "REC_709" }
}
dlg:number {
id = "gamma",
label = "Gamma Correction:",
text = string.format("%.1f", 1.0),
decimals = 5
}
dlg:button {
id = "ok",
text = "OK",
focus = true,
onclick = function()
local args = dlg.data
if args.ok then
-- Rec. 709
local rcoeff = 0.2126
local gcoeff = 0.7152
local bcoeff = 0.0722
if args.standard == "REC_601" then
rcoeff = 0.2989
gcoeff = 0.587
bcoeff = 0.114
end
local gm = args.gamma
local sprite = app.activeSprite
if sprite then
local srcLyr = app.activeLayer
if srcLyr and not srcLyr.isGroup then
local srcCel = app.activeCel
if srcCel then
local srcImg = srcCel.image
local srcItr = srcImg:pixels()
local i = 1
local px = {}
for srcClr in srcItr do
local hex = srcClr()
local b = (hex >> 0x10 & 0xff) / 255.0
local g = (hex >> 0x08 & 0xff) / 255.0
local r = (hex >> 0x00 & 0xff) / 255.0
r = r ^ gm
g = g ^ gm
b = b ^ gm
local lum = rcoeff * r
+ gcoeff * g
+ bcoeff * b
local lum255 = math.tointeger(0.5 + lum * 255.0)
local aMask = hex & 0xff000000
local grayclr = aMask | lum255 << 0x10
| lum255 << 0x08
| lum255
px[i] = grayclr
i = i + 1
end
local trgLyr = sprite:newLayer()
trgLyr.name = args.standard
local trgCel = sprite:newCel(trgLyr, srcCel.frame)
local trgImg = trgCel.image
local trgItr = trgImg:pixels()
i = 1
for trgClr in trgItr do
trgClr(px[i])
i = i + 1
end
app.activeLayer = srcLyr
app.activeCel = srcCel
app.refresh()
else
app.alert("There is no active cel.")
end
else
app.alert("The layer is a group.")
end
else
app.alert("There is no open sprite.")
end
end
end
}
dlg:button {
id = "cancel",
text = "CANCEL",
onclick = function()
dlg:close()
end
}
dlg:show { wait = false }
(There’s a bug in this script when the source image has alpha. I’m just using it for testing.)
Rec. 709 with a gamma of 1.0 gave me this. That looks like the above to me.
Rec. 601 coefficients with a gamma of 1.0 gave me this
I wasn’t sure how much the gamma mattered. The most common exponents I’ve encountered are 2.2 and 2.4 and their inverses: 1.0 / 2.2 = 0.454545…, 1./0 / 2.4 = 0.46666… Below is 709 with 1.0 / 2.2:
Does the luminance have to be corrected in relation to g (i.e., raised to 1 / g) after the r, g and b are raised to g? If so, I did that part wrong in the script above.
I don’t know if the colors of a shades widget can be updated after a dialog is opened. If so something like that could be explored as a luminance preview of colors in a palette.
A prototype:
local function foo()
local pal = app.activeSprite.palettes[1]
local len = #pal
local clrs = {}
local gamma = 1.0
local rcoeff = 0.2126
local gcoeff = 0.7152
local bcoeff = 0.0722
for i = 1, len, 1 do
local srcClr = pal:getColor(i - 1)
local rf = srcClr.red / 255.0
local gf = srcClr.green / 255.0
local bf = srcClr.blue / 255.0
rf = rf ^ gamma
gf = gf ^ gamma
bf = bf ^ gamma
local lum = rcoeff * rf
+ gcoeff * gf
+ bcoeff * bf
local lum255 = math.tointeger(0.5 + lum * 255.0)
local a255 = srcClr.alpha
clrs[i] = Color(lum255, lum255, lum255, a255)
end
return clrs
end
local function bar()
local pal = app.activeSprite.palettes[1]
local len = #pal
local clrs = {}
for i = 1, len, 1 do
local srcClr = pal:getColor(i - 1)
clrs[i] = srcClr
end
return clrs
end
local dlg = Dialog { title = "Luminance Gray" }
dlg:shades {
id = "foo",
mode="pick",
colors=foo()
}
dlg:newrow()
dlg:shades {
id = "bar",
mode="pick",
colors=bar()
}
dlg:button {
id = "cancel",
text = "CANCEL",
onclick = function()
dlg:close()
end
}
dlg:show { wait = false }
Replace foo
with whatever you think the appropriate metric should be.
Lastly, I have indeed heard of an artist who does initial drafts in grayscale and then either (1.) places layers with different blend modes over the grayscale layer or (2.) supplies the luminance as a factor to a color gradient evaluation function. Brandon James Greer has employed this technique (in Adobe Photoshop).
Best,
Jeremy