Real-image posters: match the box to the poster's shape and crop the remainder

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 23:13:47 +02:00
co-authored by Claude Sonnet 5.5
parent b3275f6a81
commit dd6f862d61
5 changed files with 153 additions and 27 deletions
+40 -6
View File
@@ -5,6 +5,7 @@ import (
"encoding/base64"
"image"
"image/png"
"math"
"math/rand"
"regexp"
"strings"
@@ -49,7 +50,7 @@ func TestKittyUploadChunksAndEncodes(t *testing.T) {
k := newKittyGraphics(rec.write)
img := noisy(200, 300) // incompressible: far more than one 4096-byte chunk of base64
id, err := k.imageID(img)
id, err := k.imageID(img, img.Bounds())
if err != nil || id != 1 {
t.Fatalf("id=%d err=%v", id, err)
}
@@ -94,10 +95,10 @@ func TestKittyUploadChunksAndEncodes(t *testing.T) {
// The same image is not uploaded twice; another one gets the next id.
n := len(rec.cmds)
if again, _ := k.imageID(img); again != id || len(rec.cmds) != n {
if again, _ := k.imageID(img, img.Bounds()); again != id || len(rec.cmds) != n {
t.Error("an uploaded image must be reused")
}
if other, _ := k.imageID(noisy(4, 4)); other != 2 {
if other, _ := k.imageID(noisy(4, 4), image.Rect(0, 0, 4, 4)); other != 2 {
t.Errorf("second image id = %d", other)
}
}
@@ -121,7 +122,8 @@ func TestKittyPlacement(t *testing.T) {
}
rec.cmds = nil
id, _ := k.imageID(noisy(2, 2))
small := noisy(2, 2)
id, _ := k.imageID(small, small.Bounds())
k.deleteAll()
if last := rec.cmds[len(rec.cmds)-1]; last != "\x1b_Ga=d,d=I,i="+itoa(int(id))+",q=2\x1b\\" {
t.Errorf("deleteAll sent %q", last)
@@ -237,7 +239,39 @@ func TestCoverViewGraphicsDrawsPlaceholders(t *testing.T) {
// A cell aspect of 2.5 (taller cells) changes the footprint of the same box.
c.cellAspect = func() float64 { return 2.5 }
c.Draw(screen)
if len(rec.cmds) != 4 || !strings.Contains(rec.cmds[3], "c=16,r=10") {
t.Errorf("after the cell aspect changed: %q", rec.cmds[2:])
// The box is now 17×10 cells, a different shape: a new crop is uploaded, the
// old upload is removed, and the image is placed afresh.
var placements []string
for _, cmd := range rec.cmds[2:] {
placements = append(placements, cmd[:min(len(cmd), 30)])
}
last := rec.cmds[len(rec.cmds)-1]
if !strings.Contains(last, "c=17,r=10") || !strings.Contains(strings.Join(rec.cmds[2:], ""), "a=d,d=I,i=1") {
t.Errorf("after the cell aspect changed: %q", placements)
}
}
func TestCropToBox(t *testing.T) {
b := image.Rect(0, 0, 167, 250) // a 2:3 poster
// A box slightly narrower than the poster: the sides are trimmed.
got := cropToBox(b, 17, 13, 2.1) // box shape 17/(13*2.1) = 0.6227 < 0.668
if got.Dy() != 250 || got.Dx() >= 167 || got.Min.X != (167-got.Dx())/2 {
t.Errorf("narrow box: %v", got)
}
if shape := float64(got.Dx()) / float64(got.Dy()); math.Abs(shape-17.0/(13*2.1)) > 0.005 {
t.Errorf("cropped shape %.4f, want %.4f", shape, 17.0/(13*2.1))
}
// A box slightly wider: top and bottom are trimmed.
got = cropToBox(b, 18, 12, 2.1) // 18/(12*2.1) = 0.714 > 0.668
if got.Dx() != 167 || got.Dy() >= 250 || got.Min.Y != (250-got.Dy())/2 {
t.Errorf("wide box: %v", got)
}
// A box of exactly the poster's shape changes nothing.
if got := cropToBox(image.Rect(0, 0, 40, 60), 20, 15, 2); got != image.Rect(0, 0, 40, 60) {
t.Errorf("exact box: %v", got)
}
// Offsets of sub-images are respected.
if got := cropToBox(image.Rect(10, 20, 50, 80), 18, 10, 2); got.Min.X != 10 || got.Max.X != 50 || got.Min.Y < 20 || got.Max.Y > 80 || got.Dy() >= 60 {
t.Errorf("offset image: %v", got)
}
}