Text + MXL logo pattern
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
// Logo plane construction for texture-sampling kernels (v210_dvd_logo.wgsl).
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
// PNG decoder registration for LoadLogoPNG.
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
// BuildLogoPlane converts an image into the packed logo plane consumed by
|
||||
// v210_dvd_logo.wgsl as read-only storage binding 2:
|
||||
//
|
||||
// logo[0] = width | height<<16
|
||||
// logo[1+dy*w+dx] = opaque<<30 | Cr<<20 | Cb<<10 | Y
|
||||
//
|
||||
// Rec.709 full-range RGB -> 10-bit studio swing, the same transform behind
|
||||
// the color-bar tables in the kernels (verified against all 21 bar values).
|
||||
// Semi-transparent pixels (alpha >= 50%) count as opaque; the rest show the
|
||||
// kernel background through.
|
||||
func BuildLogoPlane(img image.Image) ([]uint32, error) {
|
||||
b := img.Bounds()
|
||||
w, h := b.Dx(), b.Dy()
|
||||
if w <= 0 || h <= 0 || w > 0xFFFF || h > 0xFFFF {
|
||||
return nil, fmt.Errorf("logo: bad dimensions %dx%d", w, h)
|
||||
}
|
||||
plane := make([]uint32, 1+w*h)
|
||||
plane[0] = uint32(w) | uint32(h)<<16
|
||||
for yy := 0; yy < h; yy++ {
|
||||
for xx := 0; xx < w; xx++ {
|
||||
r, g, bl, a := img.At(b.Min.X+xx, b.Min.Y+yy).RGBA()
|
||||
if a < 1<<15 {
|
||||
continue // transparent: shader falls back to background
|
||||
}
|
||||
// At().RGBA() is premultiplied; unpremultiply to full-range RGB.
|
||||
rf := float64(r) / float64(a)
|
||||
gf := float64(g) / float64(a)
|
||||
bf := float64(bl) / float64(a)
|
||||
luma := 0.2126*rf + 0.7152*gf + 0.0722*bf
|
||||
yc := clamp10(64 + math.Round(876*luma))
|
||||
cb := clamp10(512 + math.Round(448*(bf-luma)/(1-0.0722)))
|
||||
cr := clamp10(512 + math.Round(448*(rf-luma)/(1-0.2126)))
|
||||
plane[1+yy*w+xx] = 1<<30 | cr<<20 | cb<<10 | yc
|
||||
}
|
||||
}
|
||||
return plane, nil
|
||||
}
|
||||
|
||||
// clamp10 keeps a 10-bit code inside the legal 4..1019 range.
|
||||
func clamp10(v float64) uint32 {
|
||||
switch {
|
||||
case v < 4:
|
||||
return 4
|
||||
case v > 1019:
|
||||
return 1019
|
||||
default:
|
||||
return uint32(v)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadLogoPNG reads a PNG file and builds the logo plane.
|
||||
func LoadLogoPNG(path string) ([]uint32, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("logo: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("logo: decode %s: %w", path, err)
|
||||
}
|
||||
return BuildLogoPlane(img)
|
||||
}
|
||||
Reference in New Issue
Block a user