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:
+28
-9
@@ -30,12 +30,19 @@ const kittyChunk = 4096 // bytes of base64 per escape sequence, the protocol max
|
||||
type kittyGraphics struct {
|
||||
write func([]byte) error // writes to the terminal, in step with the screen
|
||||
nextID uint32
|
||||
ids map[*image.RGBA]uint32
|
||||
placed map[uint32][2]int // columns and rows each image is currently placed with
|
||||
ids map[cropKey]uint32
|
||||
latest map[*image.RGBA]cropKey // the crop of each image that is currently uploaded
|
||||
placed map[uint32][2]int // columns and rows each image is currently placed with
|
||||
}
|
||||
|
||||
// cropKey identifies an uploaded image: a poster and the part of it that was sent.
|
||||
type cropKey struct {
|
||||
img *image.RGBA
|
||||
rect image.Rectangle
|
||||
}
|
||||
|
||||
func newKittyGraphics(write func([]byte) error) *kittyGraphics {
|
||||
return &kittyGraphics{write: write, nextID: 1, ids: map[*image.RGBA]uint32{}, placed: map[uint32][2]int{}}
|
||||
return &kittyGraphics{write: write, nextID: 1, ids: map[cropKey]uint32{}, latest: map[*image.RGBA]cropKey{}, placed: map[uint32][2]int{}}
|
||||
}
|
||||
|
||||
// kittyAPC builds one graphics command: ESC _ G <keys> ; <payload> ESC \.
|
||||
@@ -51,14 +58,17 @@ func kittyAPC(keys string, payload []byte) []byte {
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// imageID returns the terminal's id for the image, uploading it on first use.
|
||||
func (k *kittyGraphics) imageID(img *image.RGBA) (uint32, error) {
|
||||
if id, ok := k.ids[img]; ok {
|
||||
// imageID returns the terminal's id for the part rect of the image,
|
||||
// uploading it on first use. When the same image is needed with another crop
|
||||
// (after a resize) the earlier upload is removed.
|
||||
func (k *kittyGraphics) imageID(img *image.RGBA, rect image.Rectangle) (uint32, error) {
|
||||
key := cropKey{img, rect}
|
||||
if id, ok := k.ids[key]; ok {
|
||||
return id, nil
|
||||
}
|
||||
var data bytes.Buffer
|
||||
enc := png.Encoder{CompressionLevel: png.BestSpeed}
|
||||
if err := enc.Encode(&data, img); err != nil {
|
||||
if err := enc.Encode(&data, img.SubImage(rect)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
encoded := base64.StdEncoding.EncodeToString(data.Bytes())
|
||||
@@ -81,7 +91,15 @@ func (k *kittyGraphics) imageID(img *image.RGBA) (uint32, error) {
|
||||
rest = rest[n:]
|
||||
}
|
||||
k.nextID++
|
||||
k.ids[img] = id
|
||||
if old, ok := k.latest[img]; ok {
|
||||
if oldID, ok := k.ids[old]; ok {
|
||||
_ = k.write(kittyAPC(fmt.Sprintf("a=d,d=I,i=%d,q=2", oldID), nil))
|
||||
delete(k.placed, oldID)
|
||||
delete(k.ids, old)
|
||||
}
|
||||
}
|
||||
k.ids[key] = id
|
||||
k.latest[img] = key
|
||||
return id, nil
|
||||
}
|
||||
|
||||
@@ -110,7 +128,8 @@ func (k *kittyGraphics) deleteAll() {
|
||||
for _, id := range k.ids {
|
||||
_ = k.write(kittyAPC(fmt.Sprintf("a=d,d=I,i=%d,q=2", id), nil))
|
||||
}
|
||||
k.ids = map[*image.RGBA]uint32{}
|
||||
k.ids = map[cropKey]uint32{}
|
||||
k.latest = map[*image.RGBA]cropKey{}
|
||||
k.placed = map[uint32][2]int{}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user