Get GIF basically working
This commit is contained in:
parent
cd5dab581b
commit
7237e5cf9c
@ -5,7 +5,9 @@ package icat
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/gif"
|
||||
"kitty/tools/tui/graphics"
|
||||
"kitty/tools/utils"
|
||||
"kitty/tools/utils/images"
|
||||
"kitty/tools/utils/shm"
|
||||
|
||||
@ -14,7 +16,7 @@ import (
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
func add_frame(imgd *image_data, img image.Image, is_opaque bool) {
|
||||
func add_frame(imgd *image_data, img image.Image, is_opaque bool) *image_frame {
|
||||
if flip {
|
||||
img = imaging.FlipV(img)
|
||||
}
|
||||
@ -22,7 +24,7 @@ func add_frame(imgd *image_data, img image.Image, is_opaque bool) {
|
||||
img = imaging.FlipH(img)
|
||||
}
|
||||
b := img.Bounds()
|
||||
f := image_frame{width: b.Dx(), height: b.Dy()}
|
||||
f := image_frame{width: b.Dx(), height: b.Dy(), number: len(imgd.frames), left: b.Min.X, top: b.Min.Y}
|
||||
dest_rect := image.Rect(0, 0, f.width, f.height)
|
||||
var final_img image.Image
|
||||
|
||||
@ -53,6 +55,7 @@ func add_frame(imgd *image_data, img image.Image, is_opaque bool) {
|
||||
}
|
||||
images.PasteCenter(final_img, img, remove_alpha)
|
||||
imgd.frames = append(imgd.frames, &f)
|
||||
return &f
|
||||
}
|
||||
|
||||
func load_one_frame_image(imgd *image_data, src *opened_input) (img image.Image, is_opaque bool, err error) {
|
||||
@ -78,10 +81,49 @@ func load_one_frame_image(imgd *image_data, src *opened_input) (img image.Image,
|
||||
return
|
||||
}
|
||||
|
||||
func add_gif_frames(imgd *image_data, gf *gif.GIF) error {
|
||||
max_gap := utils.Max(0, gf.Delay...)
|
||||
min_gap := 0
|
||||
if max_gap <= 0 {
|
||||
min_gap = 1
|
||||
}
|
||||
|
||||
min_gap *= 1
|
||||
anchor_frame := 1
|
||||
for i, img := range gf.Image {
|
||||
frame := add_frame(imgd, img, img.Opaque())
|
||||
frame.delay_ms = utils.Max(min_gap, gf.Delay[i]) * 10
|
||||
if frame.delay_ms == 0 {
|
||||
frame.delay_ms = -1
|
||||
}
|
||||
if i > 0 {
|
||||
switch gf.Disposal[i] {
|
||||
case gif.DisposalNone:
|
||||
frame.compose_onto = frame.number - 1
|
||||
anchor_frame = frame.number
|
||||
case gif.DisposalBackground:
|
||||
// see https://github.com/golang/go/issues/20694
|
||||
anchor_frame = frame.number
|
||||
case gif.DisposalPrevious:
|
||||
frame.compose_onto = anchor_frame
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func render_image_with_go(imgd *image_data, src *opened_input) (err error) {
|
||||
switch imgd.format_uppercase {
|
||||
case "GIF":
|
||||
return fmt.Errorf("TODO: implement GIF decoding")
|
||||
switch {
|
||||
case imgd.format_uppercase == "GIF" && opts.Loop != 0:
|
||||
gif_frames, err := gif.DecodeAll(src.file)
|
||||
src.Rewind()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode GIF file with error: %w", err)
|
||||
}
|
||||
err = add_gif_frames(imgd, gif_frames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
img, is_opaque, err := load_one_frame_image(imgd, src)
|
||||
if err != nil {
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
@ -138,12 +139,16 @@ func (self *opened_input) Release() {
|
||||
}
|
||||
|
||||
type image_frame struct {
|
||||
filename string
|
||||
shm shm.MMap
|
||||
in_memory_bytes []byte
|
||||
filename_is_temporary bool
|
||||
width, height int
|
||||
transmission_format graphics.GRT_f
|
||||
filename string
|
||||
shm shm.MMap
|
||||
in_memory_bytes []byte
|
||||
filename_is_temporary bool
|
||||
width, height, left, top int
|
||||
transmission_format graphics.GRT_f
|
||||
compose_onto int
|
||||
number int
|
||||
disposal_background color.NRGBA
|
||||
delay_ms int
|
||||
}
|
||||
|
||||
type image_data struct {
|
||||
|
||||
@ -21,19 +21,31 @@ var _ = fmt.Print
|
||||
|
||||
func gc_for_image(imgd *image_data, frame_num int, frame *image_frame) *graphics.GraphicsCommand {
|
||||
gc := graphics.GraphicsCommand{}
|
||||
gc.SetAction(graphics.GRT_action_transmit_and_display)
|
||||
gc.SetDataWidth(uint64(frame.width)).SetDataHeight(uint64(frame.height))
|
||||
gc.SetQuiet(graphics.GRT_quiet_silent)
|
||||
if frame_num == 0 && imgd.cell_x_offset > 0 {
|
||||
gc.SetXOffset(uint64(imgd.cell_x_offset))
|
||||
}
|
||||
if z_index != 0 {
|
||||
gc.SetZIndex(z_index)
|
||||
}
|
||||
gc.SetFormat(frame.transmission_format)
|
||||
if imgd.image_number != 0 {
|
||||
gc.SetImageNumber(imgd.image_number)
|
||||
}
|
||||
gc.SetFormat(frame.transmission_format)
|
||||
if frame_num == 0 {
|
||||
gc.SetAction(graphics.GRT_action_transmit_and_display)
|
||||
if imgd.cell_x_offset > 0 {
|
||||
gc.SetXOffset(uint64(imgd.cell_x_offset))
|
||||
}
|
||||
if z_index != 0 {
|
||||
gc.SetZIndex(z_index)
|
||||
}
|
||||
} else {
|
||||
gc.SetAction(graphics.GRT_action_frame)
|
||||
gc.SetGap(int32(frame.delay_ms))
|
||||
if frame.compose_onto > 0 {
|
||||
gc.SetOverlaidFrame(uint64(frame.compose_onto))
|
||||
} else {
|
||||
bg := (uint32(frame.disposal_background.R) << 24) | (uint32(frame.disposal_background.G) << 16) | (uint32(frame.disposal_background.B) << 8) | uint32(frame.disposal_background.A)
|
||||
gc.SetBackgroundColor(bg)
|
||||
}
|
||||
gc.SetLeftEdge(uint64(frame.left)).SetTopEdge(uint64(frame.top))
|
||||
}
|
||||
return &gc
|
||||
}
|
||||
|
||||
@ -222,7 +234,9 @@ func transmit_image(imgd *image_data) {
|
||||
f = transmit_stream
|
||||
}
|
||||
if len(imgd.frames) > 1 {
|
||||
imgd.image_number = rand.Uint32()
|
||||
for imgd.image_number == 0 {
|
||||
imgd.image_number = rand.Uint32()
|
||||
}
|
||||
}
|
||||
place_cursor(imgd)
|
||||
lp.QueueWriteString("\r")
|
||||
@ -232,11 +246,38 @@ func transmit_image(imgd *image_data) {
|
||||
if imgd.move_to.x > 0 {
|
||||
lp.MoveCursorTo(imgd.move_to.x, imgd.move_to.y)
|
||||
}
|
||||
frame_control_cmd := graphics.GraphicsCommand{}
|
||||
frame_control_cmd.SetAction(graphics.GRT_action_animate).SetImageNumber(imgd.image_number)
|
||||
|
||||
for frame_num, frame := range imgd.frames {
|
||||
err := f(imgd, frame_num, frame)
|
||||
if err != nil {
|
||||
print_error("Failed to transmit %s with error: %v", imgd.source_name, err)
|
||||
print_error("\rFailed to transmit %s with error: %v", imgd.source_name, err)
|
||||
return
|
||||
}
|
||||
switch frame_num {
|
||||
case 0:
|
||||
// set gap for the first frame and number of loops for the animation
|
||||
c := frame_control_cmd
|
||||
c.SetTargetFrame(uint64(frame.number))
|
||||
c.SetGap(int32(frame.delay_ms))
|
||||
switch {
|
||||
case opts.Loop < 0:
|
||||
c.SetNumberOfLoops(1)
|
||||
case opts.Loop > 0:
|
||||
c.SetNumberOfLoops(uint64(opts.Loop) + 1)
|
||||
}
|
||||
c.WriteWithPayloadToLoop(lp, nil)
|
||||
case 1:
|
||||
c := frame_control_cmd
|
||||
c.SetAnimationControl(2) // set animation to loading mode
|
||||
c.WriteWithPayloadToLoop(lp, nil)
|
||||
}
|
||||
}
|
||||
if len(imgd.frames) > 1 {
|
||||
c := frame_control_cmd
|
||||
c.SetAnimationControl(3) // set animation to normal mode
|
||||
c.WriteWithPayloadToLoop(lp, nil)
|
||||
}
|
||||
if imgd.move_to.x == 0 {
|
||||
lp.Println() // ensure cursor is on new line
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user