|
| 1 | +-- icon loading, caching and resolution management |
| 2 | +-- |
| 3 | +-- eventually this should be generalizable to a uiprim/ script |
| 4 | +-- as manual icon handling, covering all the bases for different |
| 5 | +-- displays, optimizing for caching, atlas-slicing etc. is quite |
| 6 | +-- hairy. |
| 7 | +-- |
| 8 | +-- For now, we just do the minimal placeholder stuff needed to |
| 9 | +-- refactor the icon/font label parts of durden, then see what |
| 10 | +-- happens from there. The support sheet used for picking/binding |
| 11 | +-- should also be moved from the icons widget to here. |
| 12 | +-- |
| 13 | +-- In that direction, there are three classes of icons - labels |
| 14 | +-- textual and client defined. The first one need to be loaded |
| 15 | +-- and kept. |
| 16 | +-- |
| 17 | +-- The second needs rasterization against the target display. |
| 18 | +-- |
| 19 | +-- The third needs caching. |
| 20 | +-- |
| 21 | +local setname = gconfig_get("icon_set"); |
| 22 | + |
| 23 | +-- it is almost always useful having a circle primitive to |
| 24 | +-- build with, both for generating single colored round icons |
| 25 | +-- or to use as a mask together with resample_image |
| 26 | +icon_unit_circle = build_shader( |
| 27 | + nil, |
| 28 | + [[ |
| 29 | + uniform float radius; |
| 30 | + uniform vec3 color; |
| 31 | + varying vec2 texco; |
| 32 | +
|
| 33 | + void main() |
| 34 | + { |
| 35 | + float vis = length(texco * 2.0 - vec2(1.0)) - radius; |
| 36 | + float step = fwidth(vis); |
| 37 | + vis = smoothstep(step, -step, vis); |
| 38 | + gl_FragColor = vec4(color.rgb, vis); |
| 39 | + } |
| 40 | + ]], |
| 41 | + "iconmgr_circle" |
| 42 | +); |
| 43 | + |
| 44 | +icon_colorize = build_shader( |
| 45 | + nil, |
| 46 | + [[ |
| 47 | + uniform vec3 color; |
| 48 | + uniform sampler2D map_tu0; |
| 49 | + varying vec2 texco; |
| 50 | +
|
| 51 | + void main() |
| 52 | + { |
| 53 | + vec4 col = texture2D(map_tu0, texco); |
| 54 | + float intens = max(max(col.r, col.g), col.b); |
| 55 | + gl_FragColor = vec4(color * intens, col.a); |
| 56 | + } |
| 57 | + ]], |
| 58 | + "iconmgr_colorize" |
| 59 | +); |
| 60 | + |
| 61 | +shader_uniform(icon_unit_circle, "radius", "f", 0.5); |
| 62 | +local function synthesize_icon(w, shader) |
| 63 | + local icon = alloc_surface(w, w); |
| 64 | + if not valid_vid(icon) then |
| 65 | + return; |
| 66 | + end |
| 67 | + resample_image(icon, shader, w, w); |
| 68 | + return icon; |
| 69 | +end |
| 70 | + |
| 71 | +function icon_synthesize_src(name, w, shader, argtbl) |
| 72 | + local fn = string.format("icons/%s/%s", setname, name); |
| 73 | + local img = load_image(fn); |
| 74 | + if not valid_vid(img) then |
| 75 | + return; |
| 76 | + end |
| 77 | + for k,v in pairs(argtbl) do |
| 78 | + shader_uniform(shader, k, unpack(v)); |
| 79 | + end |
| 80 | + resample_image(img, shader, w, w); |
| 81 | + return img; |
| 82 | +end |
| 83 | + |
| 84 | +function icon_synthesize(w, shader, argtbl) |
| 85 | + for k,v in pairs(argtbl) do |
| 86 | + shader_uniform(shader, k, unpack(v)); |
| 87 | + end |
| 88 | + return synthesize_icon(w, shader); |
| 89 | +end |
| 90 | + |
| 91 | +-- The nametable mainly contains the active caches of vids based |
| 92 | +-- on a base width. Normally icons are square, though it is not |
| 93 | +-- a given. |
| 94 | +-- |
| 95 | +-- For icons where we don't need to scale but can use a function |
| 96 | +-- to generate the icon in question, the generate function is |
| 97 | +-- provided. |
| 98 | +local nametable = { |
| 99 | +}; |
| 100 | + |
| 101 | +-- take a vsym that passed validation from suppl_valid_vsym and |
| 102 | +-- return a vid that can be used for an image_sharestorage into |
| 103 | +-- a caller controlled allocation, as well as a possible shader |
| 104 | +-- identifier. An open question is if we should allow SDFs. The |
| 105 | +-- problem comes with shader used for highlights etc. |
| 106 | +function icon_lookup(vsym, px_w) |
| 107 | + if not nametable[vsym] then |
| 108 | + vsym = "placeholder"; |
| 109 | + end |
| 110 | + local ent = nametable[vsym]; |
| 111 | + |
| 112 | +-- do we have a direct match since before? |
| 113 | + if ent.widths[px_w] then |
| 114 | + return ent.widths[px_w]; |
| 115 | + end |
| 116 | + |
| 117 | +-- can we build one with it? |
| 118 | + if ent.generate then |
| 119 | + local res = ent.generate(px_w); |
| 120 | + if valid_vid(res) then |
| 121 | + ent.widths[px_w] = res; |
| 122 | + return res; |
| 123 | + end |
| 124 | + end |
| 125 | + |
| 126 | +-- find one with the least error |
| 127 | + local errv = px_w; |
| 128 | + local closest = 0; |
| 129 | + |
| 130 | + for k,v in pairs(ent) do |
| 131 | + if type(k) == "number" then |
| 132 | + local dist = math.abs(px_w - k); |
| 133 | + if dist < errv then |
| 134 | + errv = dist; |
| 135 | + closest = k; |
| 136 | + end |
| 137 | + end |
| 138 | + end |
| 139 | + |
| 140 | +-- apparently wasn't one for this specific size, fallback generator(s)? |
| 141 | + if errv > 0 and ent.generator then |
| 142 | + ent.widths[px_w] = ent.generator(px_w); |
| 143 | + end |
| 144 | + |
| 145 | +-- no solution at all? return placeholder, this shouldn't infinitely |
| 146 | +-- recurse as we always override placeholder with our own definition that |
| 147 | +-- has a synthesis option |
| 148 | + if closest == 0 then |
| 149 | + return icon_lookup("placeholder", px_w); |
| 150 | + end |
| 151 | + |
| 152 | +-- do we need to load or generate? |
| 153 | + local vid = ent.widths[closest]; |
| 154 | + if not ent.widths[closest] then |
| 155 | + if type(ent[closest]) == "string" then |
| 156 | + local fn = string.format("icons/%s/%s", setname, ent[closest]); |
| 157 | + ent.widths[closest] = load_image(fn); |
| 158 | + |
| 159 | +-- or provide some visual indicator that the icon reference was bad |
| 160 | + if (not valid_vid(ent.widths[closest])) then |
| 161 | + ent.widths[closest] = icon_lookup("placeholder", px_w); |
| 162 | + end |
| 163 | + |
| 164 | + elseif type(ent[closest]) == "function" then |
| 165 | + ent.widths[closest] = ent[closest](); |
| 166 | + else |
| 167 | +-- missing handler / malformed |
| 168 | + warning("icon_synth:bad_type=" .. type(ent[closest])); |
| 169 | + end |
| 170 | + vid = ent.widths[closest]; |
| 171 | + end |
| 172 | + |
| 173 | +-- or really panic so we don't return a broken vid |
| 174 | + return valid_vid(vid) and vid or WORLDID; |
| 175 | +end |
| 176 | + |
| 177 | +-- use a unicode symbol reference (or nametable override) |
| 178 | +-- to get an iconic or rastered representation matching the |
| 179 | +-- intended display. The display is needed as the rendertarget |
| 180 | +-- attachment for the datastore, as that covers the density. |
| 181 | +local last_u8; |
| 182 | +function icon_lookup_u8(u8, display_rt) |
| 183 | + if valid_vid(last_u8) then |
| 184 | + delete_image(last_u8); |
| 185 | + end |
| 186 | + |
| 187 | + local rt = set_context_attachment(display_rt); |
| 188 | + last_u8 = render_text({"\\f,0", u8}); |
| 189 | + set_context_attachment(rt); |
| 190 | + if not valid_vid(last_u8) then |
| 191 | + return icon_lookup("placeholder", 32); |
| 192 | + end |
| 193 | + return last_u8; |
| 194 | +end |
| 195 | + |
| 196 | +function icon_known(vsym) |
| 197 | + return vsym ~= nil and #vsym > 0 and nametable[vsym] ~= nil; |
| 198 | +end |
| 199 | + |
| 200 | +-- the enforcement on location isn't strict here, traversal |
| 201 | +-- protection is implemented on a much lower level so this is fine |
| 202 | +nametable = system_load(string.format("icons/%s.lua", setname))(); |
| 203 | + |
| 204 | +-- make sure we have some standard names |
| 205 | +if not nametable.destroy then |
| 206 | + nametable.destroy = { |
| 207 | + generate = function(w) |
| 208 | + return icon_synthesize(w, icon_unit_circle, {color = {"fff", 1.0, 0.1, 0.15}}); |
| 209 | + end |
| 210 | + }; |
| 211 | +end |
| 212 | + |
| 213 | +-- make sure we always have these |
| 214 | +if not nametable.minimize then |
| 215 | + nametable.minimize = { |
| 216 | + generate = function(w) |
| 217 | + return icon_synthesize(w, icon_unit_circle, {color = {"fff", 0.94, 0.7, 0.01}}); |
| 218 | + end, |
| 219 | + widths = {} |
| 220 | + }; |
| 221 | +end |
| 222 | + |
| 223 | +if not nametable.maximize then |
| 224 | + nametable.maximize = { |
| 225 | + generate = function(w) |
| 226 | + return icon_synthesize(w, icon_unit_circle, {color = {"fff", 0.1, 0.6, 0.1}}); |
| 227 | + end, |
| 228 | + widths = {} |
| 229 | + }; |
| 230 | +end |
| 231 | + |
| 232 | +-- reserve this one for ourselves so we always have a valid fallback |
| 233 | +nametable.placeholder = { |
| 234 | + generate = |
| 235 | + function(w) |
| 236 | + return icon_synthesize(w, icon_unit_circle, {color = {"fff", 1.0, 1.0, 1.0}}); |
| 237 | + end |
| 238 | +}; |
| 239 | + |
| 240 | +-- and safeguard so we have the width cache table |
| 241 | +for _, v in pairs(nametable) do |
| 242 | + if not v.widths then |
| 243 | + v.widths = {}; |
| 244 | + end |
| 245 | +end |
0 commit comments