From 85b7bc695629926641c7cb752fd478adfdf374f3 Mon Sep 17 00:00:00 2001 From: Marius Date: Sun, 4 Jul 2010 15:32:09 +0300 Subject: stable 2010-05-24 13:10 --- tex/context/base/attr-ini.lua | 648 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 648 insertions(+) create mode 100644 tex/context/base/attr-ini.lua (limited to 'tex/context/base/attr-ini.lua') diff --git a/tex/context/base/attr-ini.lua b/tex/context/base/attr-ini.lua new file mode 100644 index 000000000..81c2f4744 --- /dev/null +++ b/tex/context/base/attr-ini.lua @@ -0,0 +1,648 @@ +if not modules then modules = { } end modules ['attr-ini'] = { + version = 1.001, + comment = "companion to attr-ini.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 +-- we can also do the nsnone via a metatable and then also se index 0 + +local type = type +local format, gmatch = string.format, string.gmatch +local concat = table.concat +local texsprint = tex.sprint + +local ctxcatcodes = tex.ctxcatcodes +local unsetvalue = attributes.unsetvalue + +-- 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 + +nodes = nodes or { } +states = states or { } +shipouts = shipouts or { } + +-- 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 +-- +-- storage.register("colors/data", colors.strings, "colors.data") -- evaluated +-- +-- We assume that only processcolors are defined in the format. + +colors = colors or { } +colors.data = colors.data or { } +colors.values = colors.values or { } +colors.registered = colors.registered or { } + +colors.weightgray = true +colors.attribute = attributes.private('color') +colors.selector = attributes.private('colormodel') +colors.default = 1 +colors.main = nil +colors.triggering = true + +storage.register("colors/values", colors.values, "colors.values") +storage.register("colors/registered", colors.registered, "colors.registered") + +local templates = { + rgb = "r:%s:%s:%s", + cmyk = "c:%s:%s:%s:%s", + gray = "s:%s", + spot = "p:%s:%s:%s:%s" +} + +local models = { + [interfaces.variables.none] = unsetvalue, + black = unsetvalue, + bw = unsetvalue, + all = 1, + gray = 2, + rgb = 3, + cmyk = 4, +} + +colors.model = "all" + +local data = colors.data +local values = colors.values +local registered = colors.registered + +local numbers = attributes.numbers +local list = attributes.list + +local min, max, floor = math.min, math.max, math.floor + +local nodeinjections = backends.nodeinjections +local codeinjections = backends.codeinjections +local registrations = backends.registrations + +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 + +-- 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 + +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 + +function graytorgb(s) -- unweighted + return 1-s, 1-s, 1-s +end + +function hsvtogray(h,s,v) + return rgb_to_gray(hsv_to_rgb(h,s,v)) +end + +function grayto_hsv(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 key == "none" then + local d = graycolor(0) + colors.none = d + return d + end +end + +local function reviver(data,n) + local v = values[n] + local d + if not v then + local gray = graycolor(0) + d = { gray, gray, gray, gray } + logs.report("attributes","unable to revive color %s",n or "?") + else + local kind = v[1] + if kind == 2 then + local gray= graycolor(v[2]) + d = { gray, gray, gray, gray } + elseif kind == 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 kind == 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 kind == 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 + +setmetatable(colors, { __index = extender }) +setmetatable(colors.data, { __index = 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 = format(templates[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[colors.attribute][name] = color -- not grouped, so only global colors + end + return registered[stamp] +end + +function colors.value(id) + return values[id] +end + +shipouts.handle_color = nodes.install_attribute_handler { + name = "color", + namespace = colors, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.selective, + resolver = function() return colors.main end, +} + +function colors.enable() + tasks.enableaction("shipouts","shipouts.handle_color") +end + +-- transparencies + +transparencies = transparencies or { } +transparencies.registered = transparencies.registered or { } +transparencies.data = transparencies.data or { } +transparencies.values = transparencies.values or { } +transparencies.triggering = true +transparencies.attribute = attributes.private('transparency') + +storage.register("transparencies/registered", transparencies.registered, "transparencies.registered") +storage.register("transparencies/values", transparencies.values, "transparencies.values") + +local registered = transparencies.registered -- we could use a 2 dimensional table instead +local data = transparencies.data +local values = transparencies.values +local template = "%s:%s" + +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 = format(template,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 + return registered[stamp] +end + +local function extender(transparencies,key) + if key == "none" then + local d = inject_transparency(0) + transparencies.none = d + return d + end +end + +local function reviver(data,n) + 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 +end + +setmetatable(transparencies, { __index = extender }) +setmetatable(transparencies.data, { __index = reviver }) -- register if used + +-- check if there is an identity + +function transparencies.value(id) + return values[id] +end + +shipouts.handle_transparency = nodes.install_attribute_handler { + name = "transparency", + namespace = transparencies, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.process, +} + +function transparencies.enable() + tasks.enableaction("shipouts","shipouts.handle_transparency") +end + +--- colorintents: overprint / knockout + +colorintents = colorintents or { } +colorintents.data = colorintents.data or { } +colorintents.attribute = attributes.private('colorintent') + +colorintents.registered = { + 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 + +setmetatable(colorintents, { __index = extender }) +setmetatable(colorintents.data, { __index = reviver }) + +function colorintents.register(stamp) + return registered[stamp] or registered.overprint +end + +shipouts.handle_colorintent = nodes.install_attribute_handler { + name = "colorintent", + namespace = colorintents, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.process, +} + +function colorintents.enable() + tasks.enableaction("shipouts","shipouts.handle_colorintent") +end + +--- negative / positive + +negatives = negatives or { } +negatives.data = negatives.data or { } +negatives.attribute = attributes.private("negative") + +negatives.registered = { + positive = 1, + negative = 2, +} + +local data, registered = negatives.data, negatives.registered + +local function extender(negatives,key) + if key == "none" then + local d = data[1] + negatives.none = d + return d + end +end + +local function reviver(data,n) + if n == 1 then + local d = nodeinjections.positive() -- called once + data[1] = d + return d + elseif n == 2 then + local d = nodeinjections.negative() -- called once + data[2] = d + return d + end +end + +setmetatable(negatives, { __index = extender }) +setmetatable(negatives.data, { __index = reviver }) + +function negatives.register(stamp) + return registered[stamp] or registered.positive +end + +shipouts.handle_negative = nodes.install_attribute_handler { + name = "negative", + namespace = negatives, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.process, +} + +function negatives.enable() + tasks.enableaction("shipouts","shipouts.handle_negative") +end + +-- effects -- can be optimized (todo: metatables) + +effects = effects or { } +effects.data = effects.data or { } +effects.values = effects.values or { } +effects.registered = effects.registered or { } +effects.stamp = "%s:%s:%s" +effects.attribute = attributes.private("effect") + +storage.register("effects/registered", effects.registered, "effects.registered") +storage.register("effects/values", effects.values, "effects.values") + +local data, registered, values = effects.data, effects.registered, effects.values + +-- valid effects: normal inner outer both hidden (stretch,rulethickness,effect) + +local function effect(...) effect = nodeinjections.effect return effect(...) end + +local function extender(effects,key) + if key == "none" then + local d = effect(0,0,0) + effects.none = d + return d + end +end + +local function reviver(data,n) + local e = values[n] -- we could nil values[n] now but hardly needed + local d = effect(e[1],e[2],e[3]) + data[n] = d + return d +end + +setmetatable(effects, { __index = extender }) +setmetatable(effects.data, { __index = reviver }) + +function effects.register(effect,stretch,rulethickness) + local stamp = format(effects.stamp,effect,stretch,rulethickness) + local n = registered[stamp] + if not n then + n = #values + 1 + values[n] = { effect, stretch, rulethickness } + registered[stamp] = n + end + return n +end + +shipouts.handle_effect = nodes.install_attribute_handler { + name = "effect", + namespace = effects, + initializer = states.initialize, + finalizer = states.finalize, + processor = states.process, +} + +function effects.enable() + tasks.enableaction("shipouts","shipouts.handle_effect") +end + +-- layers (ugly code, due to no grouping and such); currently we use exclusive layers +-- but when we need it stacked layers might show up too; the next function based +-- approach can be replaced by static (metatable driven) resolvers + +viewerlayers = viewerlayers or { } +viewerlayers.data = viewerlayers.data or { } +viewerlayers.registered = viewerlayers.registered or { } +viewerlayers.values = viewerlayers.values or { } +viewerlayers.listwise = viewerlayers.listwise or { } +viewerlayers.attribute = attributes.private("viewerlayer") + +storage.register("viewerlayers/registered", viewerlayers.registered, "viewerlayers.registered") +storage.register("viewerlayers/values", viewerlayers.values, "viewerlayers.values") + +local data = viewerlayers.data +local values = viewerlayers.values +local listwise = viewerlayers.listwise +local registered = viewerlayers.registered +local template = "%s" + +-- stacked + +local function extender(viewerlayers,key) + if key == "none" then + local d = nodeinjections.stoplayer() + viewerlayers.none = d + return d + end +end + +local function reviver(data,n) + local d = nodeinjections.startlayer(values[n]) + data[n] = d + return d +end + +setmetatable(viewerlayers, { __index = extender }) +setmetatable(viewerlayers.data, { __index = reviver }) + +local function initializer(...) + return states.initialize(...) +end + +viewerlayers.register = function(name,lw) -- if not inimode redefine data[n] in first call + local stamp = format(template,name) + local n = registered[stamp] + if not n then + n = #values + 1 + values[n] = name + registered[stamp] = n + listwise[n] = lw or false + end + return registered[stamp] -- == n +end + +shipouts.handle_viewerlayer = nodes.install_attribute_handler { + name = "viewerlayer", + namespace = viewerlayers, + initializer = initializer, + finalizer = states.finalize, + processor = states.stacked, +} + +function viewerlayers.enable() + tasks.enableaction("shipouts","shipouts.handle_viewerlayer") +end -- cgit v1.2.3