oficina / estilo Public

sky: memoise the placeholder, hoist per-blob colour; fix a stale comment (review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 163598d9e63e5de8b22641abed05d963df79aa4d parent 841b315
2 files changed, +40 −10
  • sky/placeholder.go +37 −9
  • sky/sky.css +3 −1

CI passed — run details

CI log
=== modcache ===
all modules verified
=== vet ===
CGO_ENABLED=0 go vet ./...
=== fmt ===
=== build ===
CGO_ENABLED=0 go build -buildvcs=false ./...
=== go-test ===
CGO_ENABLED=0 go test -count=1 ./...
ok  	amadan.net/oficina/estilo.git/base	0.004s
ok  	amadan.net/oficina/estilo.git/brand	0.002s
ok  	amadan.net/oficina/estilo.git/callui	0.005s
ok  	amadan.net/oficina/estilo.git/chrome	0.159s
?   	amadan.net/oficina/estilo.git/cmd/oficina-notices	[no test files]
ok  	amadan.net/oficina/estilo.git/credits	0.005s
ok  	amadan.net/oficina/estilo.git/credits/catalog	0.004s
ok  	amadan.net/oficina/estilo.git/homeconnect	0.005s
ok  	amadan.net/oficina/estilo.git/internal/lint	0.004s
?   	amadan.net/oficina/estilo.git/internal/tokencheck	[no test files]
ok  	amadan.net/oficina/estilo.git/menu	0.007s
ok  	amadan.net/oficina/estilo.git/savestatus	0.075s
ok  	amadan.net/oficina/estilo.git/sky	0.018s
ok  	amadan.net/oficina/estilo.git/theme	0.004s
diff --git a/sky/placeholder.go b/sky/placeholder.go
index 1a8706b..5df0a36 100644
--- a/sky/placeholder.go
+++ b/sky/placeholder.go
@@ -8,6 +8,7 @@ import (
"image/png"
"math"
"strconv"
+ "sync"
)
// The placeholder is the sky in a few pixels, painted into the first
@@ -36,25 +37,52 @@ const (
phH = 20 // the SVG layers' xMidYMid slice crops them
)
+// placeholders memoises Placeholder: it is pure in (id, tone) and costs
+// milliseconds, and a page renders it on every request.
+var placeholders sync.Map // map[string]string, key id+"/"+tone
+
// Placeholder is the data: URI of the sky's placeholder for one scheme.
func Placeholder(id string, t Tone) string {
+ key := id + "/" + string(t)
+ if uri, ok := placeholders.Load(key); ok {
+ return uri.(string)
+ }
+ uri := placeholder(id, t)
+ placeholders.Store(key, uri)
+ return uri
+}
+
+func placeholder(id string, t Tone) string {
img := image.NewNRGBA(image.Rect(0, 0, phW, phH))
haze, clouds := hazeBlobs(id, t), cloudBlobs(id, t)
+ type paint struct {
+ bl blob
+ r, g, b float64
+ }
+ prep := func(bs []blob) []paint {
+ out := make([]paint, len(bs))
+ for i, bl := range bs {
+ r, g, b := hexRGB(bl.fill)
+ if phLinear {
+ r, g, b = lin(r), lin(g), lin(b)
+ }
+ out[i] = paint{bl, r, g, b}
+ }
+ return out
+ }
+ hazeP, cloudsP := prep(haze), prep(clouds)
for y := range phH {
for x := range phW {
// the pixel's centre on the 1600x640 canvas
px, py := (float64(x)+.5)*width/phW, (float64(y)+.5)*height/phH
var r, g, b, a float64 // premultiplied, 0..1
for _, layer := range []struct {
- blobs []blob
- k float64
- }{{haze, 0.7}, {clouds, 1}} {
- for _, bl := range layer.blobs {
- alpha := math.Min(1, phGain[t]*layer.k*bl.opacity*bl.at(px, py))
- cr, cg, cb := hexRGB(bl.fill)
- if phLinear {
- cr, cg, cb = lin(cr), lin(cg), lin(cb)
- }
+ paints []paint
+ k float64
+ }{{hazeP, 0.7}, {cloudsP, 1}} {
+ for _, p := range layer.paints {
+ alpha := math.Min(1, phGain[t]*layer.k*p.bl.opacity*p.bl.at(px, py))
+ cr, cg, cb := p.r, p.g, p.b
r = cr*alpha + r*(1-alpha)
g = cg*alpha + g*(1-alpha)
b = cb*alpha + b*(1-alpha)
diff --git a/sky/sky.css b/sky/sky.css
index 8e2beb0..9eb7c1e 100644
--- a/sky/sky.css
+++ b/sky/sky.css
@@ -52,7 +52,9 @@
/* One pair of haze and clouds per scheme; show the one that matches.
An explicit Appearance choice ([data-theme]) beats the OS, as it does
- for estilo's theme. Hidden images with loading="lazy" are not fetched. */
+ for estilo's theme. The other scheme's two layers are fetched too (the
+ layers are not lazy, so the first frame never waits on layout); they
+ are a couple of kilobytes and cached for a year. */
[estilo-sky-tone="dark"] { display: none; --estilo-sky-gain: 1.55; }
:root[data-theme="dark"] [estilo-sky-tone="light"] { display: none; }
:root[data-theme="dark"] [estilo-sky-tone="dark"] { display: block; }