diff options
Diffstat (limited to 'tex/context/base/attr-col.lua')
-rw-r--r-- | tex/context/base/attr-col.lua | 1076 |
1 files changed, 538 insertions, 538 deletions
diff --git a/tex/context/base/attr-col.lua b/tex/context/base/attr-col.lua index 473bf8a74..7c6b7909b 100644 --- a/tex/context/base/attr-col.lua +++ b/tex/context/base/attr-col.lua @@ -1,538 +1,538 @@ -if not modules then modules = { } end modules ['attr-col'] = {
- version = 1.001,
- comment = "companion to attr-col.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this module is being reconstructed and code will move to other places
--- we can also do the nsnone via a metatable and then also se index 0
-
--- list could as well refer to the tables (instead of numbers that
--- index into another table) .. depends on what we need
-
-local type = type
-local format = string.format
-local concat = table.concat
-local min, max, floor = math.min, math.max, math.floor
-
-local attributes, nodes, utilities, logs, backends, storage = attributes, nodes, utilities, logs, backends, storage
-local commands, context, interfaces = commands, context, interfaces
-local tex = tex
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
-local report_attributes = logs.reporter("attributes","colors")
-local report_colors = logs.reporter("colors","support")
-local report_transparencies = logs.reporter("transparencies","support")
-
--- todo: document this but first reimplement this as it reflects the early
--- days of luatex / mkiv and we have better ways now
-
--- nb: attributes: color etc is much slower than normal (marks + literals) but ...
--- nb. too many "0 g"s
-
-local states = attributes.states
-local tasks = nodes.tasks
-local nodeinjections = backends.nodeinjections
-local registrations = backends.registrations
-local unsetvalue = attributes.unsetvalue
-
-local registerstorage = storage.register
-local formatters = string.formatters
-
--- We can distinguish between rules and glyphs but it's not worth the trouble. A
--- first implementation did that and while it saves a bit for glyphs and rules, it
--- costs more resourses for transparencies. So why bother.
-
---
--- colors
---
-
--- we can also collapse the two attributes: n, n+1, n+2 and then
--- at the tex end add 0, 1, 2, but this is not faster and less
--- flexible (since sometimes we freeze color attribute values at
--- the lua end of the game)
---
--- we also need to store the colorvalues because we need then in mp
---
--- This is a compromis between speed and simplicity. We used to store the
--- values and data in one array, which made in neccessary to store the
--- converters that need node constructor into strings and evaluate them
--- at runtime (after reading from storage). Think of:
---
--- colors.strings = colors.strings or { }
---
--- if environment.initex then
--- colors.strings[color] = "return colors." .. colorspace .. "(" .. concat({...},",") .. ")"
--- end
---
--- registerstorage("attributes/colors/data", colors.strings, "attributes.colors.data") -- evaluated
---
--- We assume that only processcolors are defined in the format.
-
-attributes.colors = attributes.colors or { }
-local colors = attributes.colors
-
-local a_color = attributes.private('color')
-local a_selector = attributes.private('colormodel')
-
-colors.data = allocate()
-colors.values = colors.values or { }
-colors.registered = colors.registered or { }
-colors.weightgray = true
-colors.attribute = a_color
-colors.selector = a_selector
-colors.default = 1
-colors.main = nil
-colors.triggering = true
-colors.supported = true
-colors.model = "all"
-
-local data = colors.data
-local values = colors.values
-local registered = colors.registered
-
-local numbers = attributes.numbers
-local list = attributes.list
-
-registerstorage("attributes/colors/values", values, "attributes.colors.values")
-registerstorage("attributes/colors/registered", registered, "attributes.colors.registered")
-
-local f_colors = {
- rgb = formatters["r:%s:%s:%s"],
- cmyk = formatters["c:%s:%s:%s:%s"],
- gray = formatters["s:%s"],
- spot = formatters["p:%s:%s:%s:%s"],
-}
-
-local models = {
- [interfaces.variables.none] = unsetvalue,
- black = unsetvalue,
- bw = unsetvalue,
- all = 1,
- gray = 2,
- rgb = 3,
- cmyk = 4,
-}
-
-local function rgbtocmyk(r,g,b) -- we could reduce
- return 1-r, 1-g, 1-b, 0
-end
-
-local function cmyktorgb(c,m,y,k)
- return 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
-end
-
-local function rgbtogray(r,g,b)
- if colors.weightgray then
- return .30*r + .59*g + .11*b
- else
- return r/3 + g/3 + b/3
- end
-end
-
-local function cmyktogray(c,m,y,k)
- return rgbtogray(cmyktorgb(c,m,y,k))
-end
-
--- not critical so not needed:
---
--- local function cmyktogray(c,m,y,k)
--- local r, g, b = 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
--- if colors.weightgray then
--- return .30*r + .59*g + .11*b
--- else
--- return r/3 + g/3 + b/3
--- end
--- end
-
--- http://en.wikipedia.org/wiki/HSI_color_space
--- http://nl.wikipedia.org/wiki/HSV_(kleurruimte)
-
-local function hsvtorgb(h,s,v)
- -- h = h % 360
- local hd = h/60
- local hf = floor(hd)
- local hi = hf % 6
- -- local f = hd - hi
- local f = hd - hf
- local p = v * (1 - s)
- local q = v * (1 - f * s)
- local t = v * (1 - (1 - f) * s)
- if hi == 0 then
- return v, t, p
- elseif hi == 1 then
- return q, v, p
- elseif hi == 2 then
- return p, v, t
- elseif hi == 3 then
- return p, q, v
- elseif hi == 4 then
- return t, p, v
- elseif hi == 5 then
- return v, p, q
- else
- print("error in hsv -> rgb",hi,h,s,v)
- end
-end
-
-local function rgbtohsv(r,g,b)
- local offset, maximum, other_1, other_2
- if r >= g and r >= b then
- offset, maximum, other_1, other_2 = 0, r, g, b
- elseif g >= r and g >= b then
- offset, maximum, other_1, other_2 = 2, g, b, r
- else
- offset, maximum, other_1, other_2 = 4, b, r, g
- end
- if maximum == 0 then
- return 0, 0, 0
- end
- local minimum = other_1 < other_2 and other_1 or other_2
- if maximum == minimum then
- return 0, 0, maximum
- end
- local delta = maximum - minimum
- return (offset + (other_1-other_2)/delta)*60, delta/maximum, maximum
-end
-
-local function graytorgb(s) -- unweighted
- return 1-s, 1-s, 1-s
-end
-
-local function hsvtogray(h,s,v)
- return rgb_to_gray(hsv_to_rgb(h,s,v))
-end
-
-local function graytohsv(s)
- return 0, 0, s
-end
-
-colors.rgbtocmyk = rgbtocmyk
-colors.rgbtogray = rgbtogray
-colors.cmyktorgb = cmyktorgb
-colors.cmyktogray = cmyktogray
-colors.rgbtohsv = rgbtohsv
-colors.hsvtorgb = hsvtorgb
-colors.hsvtogray = hsvtogray
-colors.graytohsv = graytohsv
-
--- we can share some *data by using s, rgb and cmyk hashes, but
--- normally the amount of colors is not that large; storing the
--- components costs a bit of extra runtime, but we expect to gain
--- some back because we have them at hand; the number indicates the
--- default color space
-
-function colors.gray(s)
- return { 2, s, s, s, s, 0, 0, 0, 1-s }
-end
-
-function colors.rgb(r,g,b)
- local s = rgbtogray(r,g,b)
- local c, m, y, k = rgbtocmyk(r,g,b)
- return { 3, s, r, g, b, c, m, y, k }
-end
-
-function colors.cmyk(c,m,y,k)
- local s = cmyktogray(c,m,y,k)
- local r, g, b = cmyktorgb(c,m,y,k)
- return { 4, s, r, g, b, c, m, y, k }
-end
-
---~ function colors.spot(parent,f,d,p)
---~ return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p }
---~ end
-
-function colors.spot(parent,f,d,p)
- if type(p) == "number" then
- local n = list[numbers.color][parent] -- hard coded ref to color number
- if n then
- local v = values[n]
- if v then
- -- the via cmyk hack is dirty, but it scales better
- local c, m, y, k = p*v[6], p*v[7], p*v[8], p*v[8]
- local r, g, b = cmyktorgb(c,m,y,k)
- local s = cmyktogray(c,m,y,k)
- return { 5, s, r, g, b, c, m, y, k, parent, f, d, p }
- end
- end
- else
- -- todo, multitone (maybe p should be a table)
- end
- return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p }
-end
-
-local function graycolor(...) graycolor = nodeinjections.graycolor return graycolor(...) end
-local function rgbcolor (...) rgbcolor = nodeinjections.rgbcolor return rgbcolor (...) end
-local function cmykcolor(...) cmykcolor = nodeinjections.cmykcolor return cmykcolor(...) end
-local function spotcolor(...) spotcolor = nodeinjections.spotcolor return spotcolor(...) end
-
-local function extender(colors,key)
- if colors.supported and key == "none" then
- local d = graycolor(0)
- colors.none = d
- return d
- end
-end
-
-local function reviver(data,n)
- if colors.supported then
- local v = values[n]
- local d
- if not v then
- local gray = graycolor(0)
- d = { gray, gray, gray, gray }
- report_attributes("unable to revive color %a",n)
- else
- local model = colors.forcedmodel(v[1])
- if model == 2 then
- local gray= graycolor(v[2])
- d = { gray, gray, gray, gray }
- elseif model == 3 then
- local gray, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), cmykcolor(v[6],v[7],v[8],v[9])
- d = { rgb, gray, rgb, cmyk }
- elseif model == 4 then
- local gray, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), cmykcolor(v[6],v[7],v[8],v[9])
- d = { cmyk, gray, rgb, cmyk }
- elseif model == 5 then
- local spot = spotcolor(v[10],v[11],v[12],v[13])
- -- d = { spot, gray, rgb, cmyk }
- d = { spot, spot, spot, spot }
- end
- end
- data[n] = d
- return d
- end
-end
-
-setmetatableindex(colors, extender)
-setmetatableindex(colors.data, reviver)
-
-function colors.filter(n)
- return concat(data[n],":",5)
-end
-
-function colors.setmodel(name,weightgray)
- colors.model = name
- colors.default = models[name] or 1
- colors.weightgray = weightgray ~= false
- return colors.default
-end
-
-function colors.register(name, colorspace, ...) -- passing 9 vars is faster (but not called that often)
- local stamp = f_colors[colorspace](...)
- local color = registered[stamp]
- if not color then
- color = #values + 1
- values[color] = colors[colorspace](...)
- registered[stamp] = color
- -- colors.reviver(color)
- end
- if name then
- list[a_color][name] = color -- not grouped, so only global colors
- end
- return registered[stamp]
-end
-
-function colors.value(id)
- return values[id]
-end
-
-attributes.colors.handler = nodes.installattributehandler {
- name = "color",
- namespace = colors,
- initializer = states.initialize,
- finalizer = states.finalize,
- processor = states.selective,
- resolver = function() return colors.main end,
-}
-
-function colors.enable(value)
- if value == false or not colors.supported then
- tasks.disableaction("shipouts","attributes.colors.handler")
- else
- tasks.enableaction("shipouts","attributes.colors.handler")
- end
-end
-
-function colors.forcesupport(value) -- can move to attr-div
- colors.supported = value
- report_colors("color is %ssupported",value and "" or "not ")
- colors.enable(value)
-end
-
--- transparencies
-
-local a_transparency = attributes.private('transparency')
-
-attributes.transparencies = attributes.transparencies or { }
-local transparencies = attributes.transparencies
-transparencies.registered = transparencies.registered or { }
-transparencies.data = allocate()
-transparencies.values = transparencies.values or { }
-transparencies.triggering = true
-transparencies.attribute = a_transparency
-transparencies.supported = true
-
-local registered = transparencies.registered -- we could use a 2 dimensional table instead
-local data = transparencies.data
-local values = transparencies.values
-local f_transparency = formatters["%s:%s"]
-
-registerstorage("attributes/transparencies/registered", registered, "attributes.transparencies.registered")
-registerstorage("attributes/transparencies/values", values, "attributes.transparencies.values")
-
-local function inject_transparency(...)
- inject_transparency = nodeinjections.transparency
- return inject_transparency(...)
-end
-
-local function register_transparency(...)
- register_transparency = registrations.transparency
- return register_transparency(...)
-end
-
-function transparencies.register(name,a,t,force) -- name is irrelevant here (can even be nil)
- -- Force needed here for metapost converter. We could always force
- -- but then we'd end up with transparencies resources even if we
- -- would not use transparencies (but define them only). This is
- -- somewhat messy.
- local stamp = f_transparency(a,t)
- local n = registered[stamp]
- if not n then
- n = #values + 1
- values[n] = { a, t }
- registered[stamp] = n
- if force then
- register_transparency(n,a,t)
- end
- elseif force and not data[n] then
- register_transparency(n,a,t)
- end
- if name then
- list[a_transparency][name] = n -- not grouped, so only global transparencies
- end
- return registered[stamp]
-end
-
-local function extender(transparencies,key)
- if colors.supported and key == "none" then
- local d = inject_transparency(0)
- transparencies.none = d
- return d
- end
-end
-
-local function reviver(data,n)
- if transparencies.supported then
- local v = values[n]
- local d
- if not v then
- d = inject_transparency(0)
- else
- d = inject_transparency(n)
- register_transparency(n,v[1],v[2])
- end
- data[n] = d
- return d
- else
- return ""
- end
-end
-
-setmetatableindex(transparencies, extender)
-setmetatableindex(transparencies.data, reviver) -- register if used
-
--- check if there is an identity
-
-function transparencies.value(id)
- return values[id]
-end
-
-attributes.transparencies.handler = nodes.installattributehandler {
- name = "transparency",
- namespace = transparencies,
- initializer = states.initialize,
- finalizer = states.finalize,
- processor = states.process,
-}
-
-function transparencies.enable(value) -- nil is enable
- if value == false or not transparencies.supported then
- tasks.disableaction("shipouts","attributes.transparencies.handler")
- else
- tasks.enableaction("shipouts","attributes.transparencies.handler")
- end
-end
-
-function transparencies.forcesupport(value) -- can move to attr-div
- transparencies.supported = value
- report_transparencies("transparency is %ssupported",value and "" or "not ")
- transparencies.enable(value)
-end
-
---- colorintents: overprint / knockout
-
-attributes.colorintents = attributes.colorintents or { }
-local colorintents = attributes.colorintents
-colorintents.data = allocate() -- colorintents.data or { }
-colorintents.attribute = attributes.private('colorintent')
-
-colorintents.registered = allocate {
- overprint = 1,
- knockout = 2,
-}
-
-local data, registered = colorintents.data, colorintents.registered
-
-local function extender(colorintents,key)
- if key == "none" then
- local d = data[2]
- colorintents.none = d
- return d
- end
-end
-
-local function reviver(data,n)
- if n == 1 then
- local d = nodeinjections.overprint() -- called once
- data[1] = d
- return d
- elseif n == 2 then
- local d = nodeinjections.knockout() -- called once
- data[2] = d
- return d
- end
-end
-
-setmetatableindex(colorintents, extender)
-setmetatableindex(colorintents.data, reviver)
-
-function colorintents.register(stamp)
- return registered[stamp] or registered.overprint
-end
-
-colorintents.handler = nodes.installattributehandler {
- name = "colorintent",
- namespace = colorintents,
- initializer = states.initialize,
- finalizer = states.finalize,
- processor = states.process,
-}
-
-function colorintents.enable()
- tasks.enableaction("shipouts","attributes.colorintents.handler")
-end
-
--- interface
-
-commands.enablecolor = colors.enable
-commands.enabletransparency = transparencies.enable
-commands.enablecolorintents = colorintents.enable
-
-function commands.registercolor (...) context(colors .register(...)) end
-function commands.registertransparency(...) context(transparencies.register(...)) end
-function commands.registercolorintent (...) context(colorintents .register(...)) end
+if not modules then modules = { } end modules ['attr-col'] = { + version = 1.001, + comment = "companion to attr-col.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this module is being reconstructed and code will move to other places +-- we can also do the nsnone via a metatable and then also se index 0 + +-- list could as well refer to the tables (instead of numbers that +-- index into another table) .. depends on what we need + +local type = type +local format = string.format +local concat = table.concat +local min, max, floor = math.min, math.max, math.floor + +local attributes, nodes, utilities, logs, backends, storage = attributes, nodes, utilities, logs, backends, storage +local commands, context, interfaces = commands, context, interfaces +local tex = tex + +local allocate = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex + +local report_attributes = logs.reporter("attributes","colors") +local report_colors = logs.reporter("colors","support") +local report_transparencies = logs.reporter("transparencies","support") + +-- todo: document this but first reimplement this as it reflects the early +-- days of luatex / mkiv and we have better ways now + +-- nb: attributes: color etc is much slower than normal (marks + literals) but ... +-- nb. too many "0 g"s + +local states = attributes.states +local tasks = nodes.tasks +local nodeinjections = backends.nodeinjections +local registrations = backends.registrations +local unsetvalue = attributes.unsetvalue + +local registerstorage = storage.register +local formatters = string.formatters + +-- We can distinguish between rules and glyphs but it's not worth the trouble. A +-- first implementation did that and while it saves a bit for glyphs and rules, it +-- costs more resourses for transparencies. So why bother. + +-- +-- colors +-- + +-- we can also collapse the two attributes: n, n+1, n+2 and then +-- at the tex end add 0, 1, 2, but this is not faster and less +-- flexible (since sometimes we freeze color attribute values at +-- the lua end of the game) +-- +-- we also need to store the colorvalues because we need then in mp +-- +-- This is a compromis between speed and simplicity. We used to store the +-- values and data in one array, which made in neccessary to store the +-- converters that need node constructor into strings and evaluate them +-- at runtime (after reading from storage). Think of: +-- +-- colors.strings = colors.strings or { } +-- +-- if environment.initex then +-- colors.strings[color] = "return colors." .. colorspace .. "(" .. concat({...},",") .. ")" +-- end +-- +-- registerstorage("attributes/colors/data", colors.strings, "attributes.colors.data") -- evaluated +-- +-- We assume that only processcolors are defined in the format. + +attributes.colors = attributes.colors or { } +local colors = attributes.colors + +local a_color = attributes.private('color') +local a_selector = attributes.private('colormodel') + +colors.data = allocate() +colors.values = colors.values or { } +colors.registered = colors.registered or { } +colors.weightgray = true +colors.attribute = a_color +colors.selector = a_selector +colors.default = 1 +colors.main = nil +colors.triggering = true +colors.supported = true +colors.model = "all" + +local data = colors.data +local values = colors.values +local registered = colors.registered + +local numbers = attributes.numbers +local list = attributes.list + +registerstorage("attributes/colors/values", values, "attributes.colors.values") +registerstorage("attributes/colors/registered", registered, "attributes.colors.registered") + +local f_colors = { + rgb = formatters["r:%s:%s:%s"], + cmyk = formatters["c:%s:%s:%s:%s"], + gray = formatters["s:%s"], + spot = formatters["p:%s:%s:%s:%s"], +} + +local models = { + [interfaces.variables.none] = unsetvalue, + black = unsetvalue, + bw = unsetvalue, + all = 1, + gray = 2, + rgb = 3, + cmyk = 4, +} + +local function rgbtocmyk(r,g,b) -- we could reduce + return 1-r, 1-g, 1-b, 0 +end + +local function cmyktorgb(c,m,y,k) + return 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k) +end + +local function rgbtogray(r,g,b) + if colors.weightgray then + return .30*r + .59*g + .11*b + else + return r/3 + g/3 + b/3 + end +end + +local function cmyktogray(c,m,y,k) + return rgbtogray(cmyktorgb(c,m,y,k)) +end + +-- not critical so not needed: +-- +-- local function cmyktogray(c,m,y,k) +-- local r, g, b = 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k) +-- if colors.weightgray then +-- return .30*r + .59*g + .11*b +-- else +-- return r/3 + g/3 + b/3 +-- end +-- end + +-- http://en.wikipedia.org/wiki/HSI_color_space +-- http://nl.wikipedia.org/wiki/HSV_(kleurruimte) + +local function hsvtorgb(h,s,v) + -- h = h % 360 + local hd = h/60 + local hf = floor(hd) + local hi = hf % 6 + -- local f = hd - hi + local f = hd - hf + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f) * s) + if hi == 0 then + return v, t, p + elseif hi == 1 then + return q, v, p + elseif hi == 2 then + return p, v, t + elseif hi == 3 then + return p, q, v + elseif hi == 4 then + return t, p, v + elseif hi == 5 then + return v, p, q + else + print("error in hsv -> rgb",hi,h,s,v) + end +end + +local function rgbtohsv(r,g,b) + local offset, maximum, other_1, other_2 + if r >= g and r >= b then + offset, maximum, other_1, other_2 = 0, r, g, b + elseif g >= r and g >= b then + offset, maximum, other_1, other_2 = 2, g, b, r + else + offset, maximum, other_1, other_2 = 4, b, r, g + end + if maximum == 0 then + return 0, 0, 0 + end + local minimum = other_1 < other_2 and other_1 or other_2 + if maximum == minimum then + return 0, 0, maximum + end + local delta = maximum - minimum + return (offset + (other_1-other_2)/delta)*60, delta/maximum, maximum +end + +local function graytorgb(s) -- unweighted + return 1-s, 1-s, 1-s +end + +local function hsvtogray(h,s,v) + return rgb_to_gray(hsv_to_rgb(h,s,v)) +end + +local function graytohsv(s) + return 0, 0, s +end + +colors.rgbtocmyk = rgbtocmyk +colors.rgbtogray = rgbtogray +colors.cmyktorgb = cmyktorgb +colors.cmyktogray = cmyktogray +colors.rgbtohsv = rgbtohsv +colors.hsvtorgb = hsvtorgb +colors.hsvtogray = hsvtogray +colors.graytohsv = graytohsv + +-- we can share some *data by using s, rgb and cmyk hashes, but +-- normally the amount of colors is not that large; storing the +-- components costs a bit of extra runtime, but we expect to gain +-- some back because we have them at hand; the number indicates the +-- default color space + +function colors.gray(s) + return { 2, s, s, s, s, 0, 0, 0, 1-s } +end + +function colors.rgb(r,g,b) + local s = rgbtogray(r,g,b) + local c, m, y, k = rgbtocmyk(r,g,b) + return { 3, s, r, g, b, c, m, y, k } +end + +function colors.cmyk(c,m,y,k) + local s = cmyktogray(c,m,y,k) + local r, g, b = cmyktorgb(c,m,y,k) + return { 4, s, r, g, b, c, m, y, k } +end + +--~ function colors.spot(parent,f,d,p) +--~ return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p } +--~ end + +function colors.spot(parent,f,d,p) + if type(p) == "number" then + local n = list[numbers.color][parent] -- hard coded ref to color number + if n then + local v = values[n] + if v then + -- the via cmyk hack is dirty, but it scales better + local c, m, y, k = p*v[6], p*v[7], p*v[8], p*v[8] + local r, g, b = cmyktorgb(c,m,y,k) + local s = cmyktogray(c,m,y,k) + return { 5, s, r, g, b, c, m, y, k, parent, f, d, p } + end + end + else + -- todo, multitone (maybe p should be a table) + end + return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p } +end + +local function graycolor(...) graycolor = nodeinjections.graycolor return graycolor(...) end +local function rgbcolor (...) rgbcolor = nodeinjections.rgbcolor return rgbcolor (...) end +local function cmykcolor(...) cmykcolor = nodeinjections.cmykcolor return cmykcolor(...) end +local function spotcolor(...) spotcolor = nodeinjections.spotcolor return spotcolor(...) end + +local function extender(colors,key) + if colors.supported and key == "none" then + local d = graycolor(0) + colors.none = d + return d + end +end + +local function reviver(data,n) + if colors.supported then + local v = values[n] + local d + if not v then + local gray = graycolor(0) + d = { gray, gray, gray, gray } + report_attributes("unable to revive color %a",n) + else + local model = colors.forcedmodel(v[1]) + if model == 2 then + local gray= graycolor(v[2]) + d = { gray, gray, gray, gray } + elseif model == 3 then + local gray, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), cmykcolor(v[6],v[7],v[8],v[9]) + d = { rgb, gray, rgb, cmyk } + elseif model == 4 then + local gray, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), cmykcolor(v[6],v[7],v[8],v[9]) + d = { cmyk, gray, rgb, cmyk } + elseif model == 5 then + local spot = spotcolor(v[10],v[11],v[12],v[13]) + -- d = { spot, gray, rgb, cmyk } + d = { spot, spot, spot, spot } + end + end + data[n] = d + return d + end +end + +setmetatableindex(colors, extender) +setmetatableindex(colors.data, reviver) + +function colors.filter(n) + return concat(data[n],":",5) +end + +function colors.setmodel(name,weightgray) + colors.model = name + colors.default = models[name] or 1 + colors.weightgray = weightgray ~= false + return colors.default +end + +function colors.register(name, colorspace, ...) -- passing 9 vars is faster (but not called that often) + local stamp = f_colors[colorspace](...) + local color = registered[stamp] + if not color then + color = #values + 1 + values[color] = colors[colorspace](...) + registered[stamp] = color + -- colors.reviver(color) + end + if name then + list[a_color][name] = color -- not grouped, so only global colors + end + return registered[stamp] +end + +function colors.value(id) + return values[id] +end + +attributes.colors.handler = nodes.installattributehandler { + name = "color", + namespace = colors, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.selective, + resolver = function() return colors.main end, +} + +function colors.enable(value) + if value == false or not colors.supported then + tasks.disableaction("shipouts","attributes.colors.handler") + else + tasks.enableaction("shipouts","attributes.colors.handler") + end +end + +function colors.forcesupport(value) -- can move to attr-div + colors.supported = value + report_colors("color is %ssupported",value and "" or "not ") + colors.enable(value) +end + +-- transparencies + +local a_transparency = attributes.private('transparency') + +attributes.transparencies = attributes.transparencies or { } +local transparencies = attributes.transparencies +transparencies.registered = transparencies.registered or { } +transparencies.data = allocate() +transparencies.values = transparencies.values or { } +transparencies.triggering = true +transparencies.attribute = a_transparency +transparencies.supported = true + +local registered = transparencies.registered -- we could use a 2 dimensional table instead +local data = transparencies.data +local values = transparencies.values +local f_transparency = formatters["%s:%s"] + +registerstorage("attributes/transparencies/registered", registered, "attributes.transparencies.registered") +registerstorage("attributes/transparencies/values", values, "attributes.transparencies.values") + +local function inject_transparency(...) + inject_transparency = nodeinjections.transparency + return inject_transparency(...) +end + +local function register_transparency(...) + register_transparency = registrations.transparency + return register_transparency(...) +end + +function transparencies.register(name,a,t,force) -- name is irrelevant here (can even be nil) + -- Force needed here for metapost converter. We could always force + -- but then we'd end up with transparencies resources even if we + -- would not use transparencies (but define them only). This is + -- somewhat messy. + local stamp = f_transparency(a,t) + local n = registered[stamp] + if not n then + n = #values + 1 + values[n] = { a, t } + registered[stamp] = n + if force then + register_transparency(n,a,t) + end + elseif force and not data[n] then + register_transparency(n,a,t) + end + if name then + list[a_transparency][name] = n -- not grouped, so only global transparencies + end + return registered[stamp] +end + +local function extender(transparencies,key) + if colors.supported and key == "none" then + local d = inject_transparency(0) + transparencies.none = d + return d + end +end + +local function reviver(data,n) + if transparencies.supported then + local v = values[n] + local d + if not v then + d = inject_transparency(0) + else + d = inject_transparency(n) + register_transparency(n,v[1],v[2]) + end + data[n] = d + return d + else + return "" + end +end + +setmetatableindex(transparencies, extender) +setmetatableindex(transparencies.data, reviver) -- register if used + +-- check if there is an identity + +function transparencies.value(id) + return values[id] +end + +attributes.transparencies.handler = nodes.installattributehandler { + name = "transparency", + namespace = transparencies, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.process, +} + +function transparencies.enable(value) -- nil is enable + if value == false or not transparencies.supported then + tasks.disableaction("shipouts","attributes.transparencies.handler") + else + tasks.enableaction("shipouts","attributes.transparencies.handler") + end +end + +function transparencies.forcesupport(value) -- can move to attr-div + transparencies.supported = value + report_transparencies("transparency is %ssupported",value and "" or "not ") + transparencies.enable(value) +end + +--- colorintents: overprint / knockout + +attributes.colorintents = attributes.colorintents or { } +local colorintents = attributes.colorintents +colorintents.data = allocate() -- colorintents.data or { } +colorintents.attribute = attributes.private('colorintent') + +colorintents.registered = allocate { + overprint = 1, + knockout = 2, +} + +local data, registered = colorintents.data, colorintents.registered + +local function extender(colorintents,key) + if key == "none" then + local d = data[2] + colorintents.none = d + return d + end +end + +local function reviver(data,n) + if n == 1 then + local d = nodeinjections.overprint() -- called once + data[1] = d + return d + elseif n == 2 then + local d = nodeinjections.knockout() -- called once + data[2] = d + return d + end +end + +setmetatableindex(colorintents, extender) +setmetatableindex(colorintents.data, reviver) + +function colorintents.register(stamp) + return registered[stamp] or registered.overprint +end + +colorintents.handler = nodes.installattributehandler { + name = "colorintent", + namespace = colorintents, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.process, +} + +function colorintents.enable() + tasks.enableaction("shipouts","attributes.colorintents.handler") +end + +-- interface + +commands.enablecolor = colors.enable +commands.enabletransparency = transparencies.enable +commands.enablecolorintents = colorintents.enable + +function commands.registercolor (...) context(colors .register(...)) end +function commands.registertransparency(...) context(transparencies.register(...)) end +function commands.registercolorintent (...) context(colorintents .register(...)) end |