Add support for animations to icat

This commit is contained in:
Kovid Goyal
2021-02-04 14:18:47 +05:30
parent f18a56682f
commit 0f18fedf13
2 changed files with 93 additions and 8 deletions

View File

@@ -5,8 +5,8 @@
import contextlib
import os
import re
import socket
import signal
import socket
import sys
import zlib
from base64 import standard_b64encode
@@ -17,18 +17,18 @@ from typing import (
Dict, Generator, List, NamedTuple, Optional, Pattern, Tuple, Union
)
from kitty.guess_mime_type import guess_type
from kitty.cli import parse_args
from kitty.cli_stub import IcatCLIOptions
from kitty.constants import appname
from kitty.guess_mime_type import guess_type
from kitty.typing import GRT_f, GRT_t
from kitty.utils import (
TTYIO, ScreenSize, ScreenSizeGetter, fit_image, screen_size_function
)
from ..tui.images import (
ConvertFailed, GraphicsCommand, NoImageMagick, OpenFailed, render_as_single_image, fsenc,
identify
ConvertFailed, Dispose, GraphicsCommand, NoImageMagick, OpenFailed,
RenderedImage, fsenc, identify, render_as_single_image, render_image
)
from ..tui.operations import clear_images_on_screen, raw_mode
@@ -207,7 +207,8 @@ def show(
fmt: 'GRT_f',
transmit_mode: 'GRT_t' = 't',
align: str = 'center',
place: Optional['Place'] = None
place: Optional['Place'] = None,
use_number: int = 0
) -> None:
cmd = GraphicsCommand()
cmd.a = 'T'
@@ -215,6 +216,9 @@ def show(
cmd.s = width
cmd.v = height
cmd.z = zindex
if use_number:
cmd.I = use_number # noqa
cmd.q = 2
if place:
set_cursor_for_place(place, cmd, width, height, align)
else:
@@ -232,6 +236,56 @@ def show(
write_chunked(cmd, data)
def show_frames(frame_data: RenderedImage, use_number: int) -> None:
transmit_cmd = GraphicsCommand()
transmit_cmd.a = 'f'
transmit_cmd.I = use_number # noqa
transmit_cmd.q = 2
if can_transfer_with_files:
transmit_cmd.t = 't'
transmit_cmd.f = 24 if frame_data.mode == 'rgb' else 32
def control(frame_number: int = 0, loops: Optional[int] = None, gap: Optional[int] = 0, start_animation: bool = False) -> None:
cmd = GraphicsCommand()
cmd.a = 'a'
cmd.I = use_number # noqa
cmd.r = frame_number
if loops is not None:
cmd.v = loops + 1
if gap is not None:
cmd.z = gap if gap > 0 else -1
if start_animation:
cmd.s = 1
write_gr_cmd(cmd)
anchor_frame = 0
for frame in frame_data.frames:
frame_number = frame.index + 1
if frame.dispose < Dispose.previous:
anchor_frame = frame_number
if frame_number == 1:
control(frame_number, gap=frame.gap, loops=1)
continue
if frame.dispose is Dispose.previous:
if anchor_frame != frame_number:
transmit_cmd.c = anchor_frame
else:
transmit_cmd.c = (frame_number - 1) if frame.needs_blend else 0
transmit_cmd.s = frame.width
transmit_cmd.v = frame.height
transmit_cmd.x = frame.canvas_x
transmit_cmd.y = frame.canvas_y
transmit_cmd.z = frame.gap if frame.gap > 0 else -1
if can_transfer_with_files:
write_gr_cmd(transmit_cmd, standard_b64encode(os.path.abspath(frame.path).encode(fsenc)))
else:
with open(frame.path, 'rb') as f:
data = f.read()
write_chunked(transmit_cmd, data)
control(loops=0, start_animation=True)
def parse_z_index(val: str) -> int:
origin = 0
if val.startswith('--'):
@@ -254,6 +308,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
needs_scaling = m.width > available_width or m.height > available_height
needs_scaling = needs_scaling or args.scale_up
file_removed = False
use_number = 0
if m.fmt == 'png' and not needs_scaling:
outfile = path
transmit_mode: 'GRT_t' = 't' if is_tempfile else 'f'
@@ -263,8 +318,25 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
else:
fmt = 24 if m.mode == 'rgb' else 32
transmit_mode = 't'
outfile, width, height = render_as_single_image(path, m, available_width, available_height, args.scale_up)
show(outfile, width, height, parsed_opts.z_index, fmt, transmit_mode, align=args.align, place=parsed_opts.place)
if len(m) == 1:
outfile, width, height = render_as_single_image(path, m, available_width, available_height, args.scale_up)
else:
import struct
use_number = max(1, struct.unpack('@I', os.urandom(4))[0])
with NamedTemporaryFile() as f:
prefix = f.name
frame_data = render_image(path, prefix, m, available_width, available_height, args.scale_up)
outfile, width, height = frame_data.frames[0].path, frame_data.width, frame_data.height
show(
outfile, width, height, parsed_opts.z_index, fmt, transmit_mode,
align=args.align, place=parsed_opts.place, use_number=use_number
)
if use_number:
show_frames(frame_data, use_number)
if not can_transfer_with_files:
for fr in frame_data.frames:
with contextlib.suppress(FileNotFoundError):
os.unlink(fr.path)
if not args.place:
print() # ensure cursor is on a new line
return file_removed