package check import ( "fmt" "math" "regexp" "strconv" ) // canonColours and what it reaches, from rastrillo's ui/derived_test.go: // untagged, so the CSS gates that read text and the browser drives that // read computed styles both use it. The derived-colour evaluator it sat // beside (evalColour, rgba, accentSupportsBlock) is ported with the // gate that uses it. // oklchClip renders OKLCh as eight-bit sRGB the way Chromium paints an // out-of-gamut colour: each linear channel clipped to 0..1. Lightness // and chroma are both kept, so a clipped colour is brighter than the // one asked for; that is what the dark fill's lightness drop exists to // absorb (day.css, the @supports comment). func oklchClip(l, c, hue float64) [3]uint8 { rad := hue * math.Pi / 180 lin := oklabToLinear(l, c*math.Cos(rad), c*math.Sin(rad)) var v [3]uint8 for i, ch := range lin { n := math.Round(linearToSRGB(math.Min(1, math.Max(0, ch))) * 255) v[i] = uint8(math.Min(255, math.Max(0, n))) } return v } var ( srgbSer = regexp.MustCompile(`color\(srgb ([0-9.e-]+) ([0-9.e-]+) ([0-9.e-]+)(?: / ([0-9.e-]+))?\)`) oklchSer = regexp.MustCompile(`oklch\(([0-9.e-]+) ([0-9.e-]+) ([0-9.e-]+)(?: / ([0-9.e-]+))?\)`) ) // canonColours rewrites the colour functions Chromium serialises a // derived colour as, color(srgb …) for a color-mix() and oklch(…) for a // relative colour, into the rgb()/rgba() the drives compare against. // A computed value that is a legacy colour is already in that form. // Without this every literal in the depth drives would have to be the // six-decimal serialisation of a float, which changes with the engine's // printing rather than with the colour. func canonColours(s string) string { s = srgbSer.ReplaceAllStringFunc(s, func(m string) string { p := srgbSer.FindStringSubmatch(m) var ch [3]int for i := range ch { f, _ := strconv.ParseFloat(p[i+1], 64) ch[i] = int(math.Round(f * 255)) } if p[4] == "" { return fmt.Sprintf("rgb(%d, %d, %d)", ch[0], ch[1], ch[2]) } return fmt.Sprintf("rgba(%d, %d, %d, %s)", ch[0], ch[1], ch[2], p[4]) }) return oklchSer.ReplaceAllStringFunc(s, func(m string) string { p := oklchSer.FindStringSubmatch(m) l, _ := strconv.ParseFloat(p[1], 64) c, _ := strconv.ParseFloat(p[2], 64) h, _ := strconv.ParseFloat(p[3], 64) rgb := oklchClip(l, c, h) if p[4] == "" { return fmt.Sprintf("rgb(%d, %d, %d)", rgb[0], rgb[1], rgb[2]) } return fmt.Sprintf("rgba(%d, %d, %d, %s)", rgb[0], rgb[1], rgb[2], p[4]) }) }