Text + MXL logo pattern

This commit is contained in:
Dmitry Sergeev
2026-09-13 16:33:50 +03:00
parent 94ff41a124
commit 97dec92b8b
15 changed files with 999 additions and 23 deletions
+156
View File
@@ -0,0 +1,156 @@
package generator
import (
"encoding/binary"
"path/filepath"
"testing"
"golang.org/x/image/font"
)
func testFace(t *testing.T, size float64) font.Face {
t.Helper()
face, err := LoadFace(filepath.Join("..", "..", "assets", "fonts",
"JetBrainsMonoNLNerdFontMono-Regular.ttf"), size)
if err != nil {
t.Fatalf("LoadFace: %v", err)
}
t.Cleanup(func() { face.Close() })
return face
}
func TestNewTextOverlay(t *testing.T) {
const frameW, frameH = 1920, 1080
o, err := NewTextOverlay("AB", frameW, frameH, testFace(t, 48))
if err != nil {
t.Fatalf("NewTextOverlay: %v", err)
}
if o.w%6 != 0 {
t.Fatalf("w=%d not a multiple of 6", o.w)
}
if o.x%6 != 0 {
t.Fatalf("x=%d not a multiple of 6", o.x)
}
if center := o.x + o.w/2; center < frameW/2-3 || center > frameW/2+3 {
t.Fatalf("box center %d not near frame center %d", center, frameW/2)
}
if o.y != 48 {
t.Fatalf("y=%d, want 48", o.y)
}
if o.blocks == nil || len(o.blocks) != o.h*(o.w/6)*16 {
t.Fatalf("pre-packed tile has wrong size")
}
if o.cov[2*o.w+2] != 0 {
t.Fatalf("padding pixel (2,2) covered: %d", o.cov[2*o.w+2])
}
nonzero, full := 0, 0
for _, c := range o.cov {
if c > 0 {
nonzero++
}
if c == 255 {
full++
}
}
if nonzero < 100 {
t.Fatalf("too few covered pixels: %d", nonzero)
}
if full == 0 {
t.Fatalf("no fully covered (255) pixels")
}
// Text without descenders ("AB") must leave the bottom quarter empty;
// text with a descender ("Ag") must have coverage there.
for _, tc := range []struct {
text string
wantAny bool
}{
{"AB", false},
{"Ag", true},
} {
o, err := NewTextOverlay(tc.text, frameW, frameH, testFace(t, 48))
if err != nil {
t.Fatalf("NewTextOverlay(%q): %v", tc.text, err)
}
any := false
for row := o.h * 3 / 4; row < o.h; row++ {
for col := 0; col < o.w; col++ {
if o.cov[row*o.w+col] > 0 {
any = true
}
}
}
if any != tc.wantAny {
t.Fatalf("%q: coverage in bottom quarter = %v, want %v", tc.text, any, tc.wantAny)
}
}
}
func TestTextOverlayApplyV210(t *testing.T) {
const frameW, frameH = 1920, 1080
o, err := NewTextOverlay("MXL", frameW, frameH, testFace(t, 48))
if err != nil {
t.Fatalf("NewTextOverlay: %v", err)
}
frame := make([]byte, frameW*frameH*8/3)
for i := range frame {
frame[i] = 0x5A // marker: untouched regions must survive
}
if err := o.ApplyV210(frame); err != nil {
t.Fatalf("ApplyV210: %v", err)
}
sample := func(x, y int) (yc, cb, cr uint32) {
p := y*frameW + x
off := (p / 6) * 16
w0 := binary.LittleEndian.Uint32(frame[off:])
w1 := binary.LittleEndian.Uint32(frame[off+4:])
w2 := binary.LittleEndian.Uint32(frame[off+8:])
w3 := binary.LittleEndian.Uint32(frame[off+12:])
switch p % 6 {
case 0:
return (w0 >> 10) & 0x3FF, w0 & 0x3FF, (w0 >> 20) & 0x3FF
case 1:
return w1 & 0x3FF, w0 & 0x3FF, (w0 >> 20) & 0x3FF
case 2:
return (w1 >> 20) & 0x3FF, (w1 >> 10) & 0x3FF, w2 & 0x3FF
case 3:
return (w2 >> 10) & 0x3FF, (w1 >> 10) & 0x3FF, w2 & 0x3FF
case 4:
return w3 & 0x3FF, (w2 >> 20) & 0x3FF, (w3 >> 10) & 0x3FF
default:
return (w3 >> 20) & 0x3FF, (w2 >> 20) & 0x3FF, (w3 >> 10) & 0x3FF
}
}
// Every box pixel: Y from coverage, neutral chroma at even columns.
for row := 0; row < o.h; row++ {
for col := 0; col < o.w; col++ {
wantY := uint32(64 + (int(o.cov[row*o.w+col])*876+127)/255)
yc, cb, cr := sample(o.x+col, o.y+row)
if yc != wantY {
t.Fatalf("box pixel (%d,%d): Y=%d, want %d", o.x+col, o.y+row, yc, wantY)
}
if (o.x+col)%2 == 0 && (cb != 512 || cr != 512) {
t.Fatalf("box pixel (%d,%d): Cb=%d Cr=%d, want 512/512", o.x+col, o.y+row, cb, cr)
}
}
}
// Blocks just outside the box (right edge and below) must be untouched.
for _, p := range [][2]int{
{o.x + o.w, o.y}, // right of box, block-aligned
{o.x, o.y + o.h}, // below box
{o.x + o.w, o.y + o.h}, // corner
{100, 1000}, // far away
} {
off := ((p[1]*frameW + p[0]) / 6) * 16
for i := 0; i < 16; i++ {
if frame[off+i] != 0x5A {
t.Fatalf("block at (%d,%d) modified outside the box", p[0], p[1])
}
}
}
}