icat kitten: Add options to mirror images and remove their transparency before displaying them
Fixes #4513
This commit is contained in:
parent
dd31ee60f2
commit
4ce6d718c9
@ -102,6 +102,9 @@ Detailed list of changes
|
|||||||
|
|
||||||
- Draw the dots for braille characters more evenly spaced at all font sizes (:iss:`4499`)
|
- Draw the dots for braille characters more evenly spaced at all font sizes (:iss:`4499`)
|
||||||
|
|
||||||
|
- icat kitten: Add options to mirror images and remove their transparency
|
||||||
|
before displaying them (:iss:`4513`)
|
||||||
|
|
||||||
- macOS: Respect the users system-wide global keyboard shortcut preferences
|
- macOS: Respect the users system-wide global keyboard shortcut preferences
|
||||||
(:iss:`4501`)
|
(:iss:`4501`)
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from kitty.cli import parse_args
|
|||||||
from kitty.cli_stub import IcatCLIOptions
|
from kitty.cli_stub import IcatCLIOptions
|
||||||
from kitty.constants import appname
|
from kitty.constants import appname
|
||||||
from kitty.guess_mime_type import guess_type
|
from kitty.guess_mime_type import guess_type
|
||||||
|
from kitty.rgb import to_color
|
||||||
from kitty.types import run_once
|
from kitty.types import run_once
|
||||||
from kitty.typing import GRT_f, GRT_t
|
from kitty.typing import GRT_f, GRT_t
|
||||||
from kitty.utils import (
|
from kitty.utils import (
|
||||||
@ -55,6 +56,19 @@ are smaller than the specified area to be scaled up to use as much
|
|||||||
of the specified area as possible.
|
of the specified area as possible.
|
||||||
|
|
||||||
|
|
||||||
|
--background
|
||||||
|
default=none
|
||||||
|
Specify a background color, this will cause transparent images to be composited on
|
||||||
|
top of the specified color.
|
||||||
|
|
||||||
|
|
||||||
|
--mirror
|
||||||
|
default=none
|
||||||
|
type=choices
|
||||||
|
choices=none,horizontal,vertical,both
|
||||||
|
Mirror the image about a horizontal or vertical axis or both.
|
||||||
|
|
||||||
|
|
||||||
--clear
|
--clear
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Remove all images currently displayed on the screen.
|
Remove all images currently displayed on the screen.
|
||||||
@ -307,6 +321,9 @@ class ParsedOpts:
|
|||||||
|
|
||||||
place: Optional['Place'] = None
|
place: Optional['Place'] = None
|
||||||
z_index: int = 0
|
z_index: int = 0
|
||||||
|
remove_alpha: str = ''
|
||||||
|
flip: bool = False
|
||||||
|
flop: bool = False
|
||||||
|
|
||||||
|
|
||||||
def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfile: bool) -> bool:
|
def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfile: bool) -> bool:
|
||||||
@ -316,9 +333,10 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
|
|||||||
available_height = parsed_opts.place.height * (ss.height // ss.rows) if parsed_opts.place else 10 * m.height
|
available_height = parsed_opts.place.height * (ss.height // ss.rows) if parsed_opts.place else 10 * m.height
|
||||||
needs_scaling = m.width > available_width or m.height > available_height
|
needs_scaling = m.width > available_width or m.height > available_height
|
||||||
needs_scaling = needs_scaling or args.scale_up
|
needs_scaling = needs_scaling or args.scale_up
|
||||||
|
needs_conversion = needs_scaling or bool(parsed_opts.remove_alpha) or parsed_opts.flip or parsed_opts.flop
|
||||||
file_removed = False
|
file_removed = False
|
||||||
use_number = 0
|
use_number = 0
|
||||||
if m.fmt == 'png' and not needs_scaling:
|
if m.fmt == 'png' and not needs_conversion:
|
||||||
outfile = path
|
outfile = path
|
||||||
transmit_mode: 'GRT_t' = 't' if is_tempfile else 'f'
|
transmit_mode: 'GRT_t' = 't' if is_tempfile else 'f'
|
||||||
fmt: 'GRT_f' = 100
|
fmt: 'GRT_f' = 100
|
||||||
@ -328,13 +346,17 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
|
|||||||
fmt = 24 if m.mode == 'rgb' else 32
|
fmt = 24 if m.mode == 'rgb' else 32
|
||||||
transmit_mode = 't'
|
transmit_mode = 't'
|
||||||
if len(m) == 1 or args.loop == 0:
|
if len(m) == 1 or args.loop == 0:
|
||||||
outfile, width, height = render_as_single_image(path, m, available_width, available_height, args.scale_up)
|
outfile, width, height = render_as_single_image(
|
||||||
|
path, m, available_width, available_height, args.scale_up,
|
||||||
|
remove_alpha=parsed_opts.remove_alpha, flip=parsed_opts.flip, flop=parsed_opts.flop)
|
||||||
else:
|
else:
|
||||||
import struct
|
import struct
|
||||||
use_number = max(1, struct.unpack('@I', os.urandom(4))[0])
|
use_number = max(1, struct.unpack('@I', os.urandom(4))[0])
|
||||||
with NamedTemporaryFile() as f:
|
with NamedTemporaryFile() as f:
|
||||||
prefix = f.name
|
prefix = f.name
|
||||||
frame_data = render_image(path, prefix, m, available_width, available_height, args.scale_up)
|
frame_data = render_image(
|
||||||
|
path, prefix, m, available_width, available_height, args.scale_up,
|
||||||
|
remove_alpha=parsed_opts.remove_alpha, flip=parsed_opts.flip, flop=parsed_opts.flop)
|
||||||
outfile, width, height = frame_data.frames[0].path, frame_data.width, frame_data.height
|
outfile, width, height = frame_data.frames[0].path, frame_data.width, frame_data.height
|
||||||
show(
|
show(
|
||||||
outfile, width, height, parsed_opts.z_index, fmt, transmit_mode,
|
outfile, width, height, parsed_opts.z_index, fmt, transmit_mode,
|
||||||
@ -526,6 +548,13 @@ def main(args: List[str] = sys.argv) -> None:
|
|||||||
parsed_opts.z_index = parse_z_index(cli_opts.z_index)
|
parsed_opts.z_index = parse_z_index(cli_opts.z_index)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise SystemExit(f'Not a valid z-index specification: {cli_opts.z_index}')
|
raise SystemExit(f'Not a valid z-index specification: {cli_opts.z_index}')
|
||||||
|
try:
|
||||||
|
if cli_opts.background != 'none':
|
||||||
|
parsed_opts.remove_alpha = to_color(cli_opts.background, validate=True).as_sharp
|
||||||
|
except ValueError:
|
||||||
|
raise SystemExit(f'Not a valid color specification: {cli_opts.background}')
|
||||||
|
parsed_opts.flip = cli_opts.mirror in ('both', 'vertical')
|
||||||
|
parsed_opts.flop = cli_opts.mirror in ('both', 'horizontal')
|
||||||
|
|
||||||
if cli_opts.detect_support:
|
if cli_opts.detect_support:
|
||||||
if not detect_support(wait_for=cli_opts.detection_timeout, silent=True):
|
if not detect_support(wait_for=cli_opts.detection_timeout, silent=True):
|
||||||
|
|||||||
@ -189,7 +189,9 @@ def render_image(
|
|||||||
m: ImageData,
|
m: ImageData,
|
||||||
available_width: int, available_height: int,
|
available_width: int, available_height: int,
|
||||||
scale_up: bool,
|
scale_up: bool,
|
||||||
only_first_frame: bool = False
|
only_first_frame: bool = False,
|
||||||
|
remove_alpha: str = '',
|
||||||
|
flip: bool = False, flop: bool = False,
|
||||||
) -> RenderedImage:
|
) -> RenderedImage:
|
||||||
import tempfile
|
import tempfile
|
||||||
has_multiple_frames = len(m) > 1
|
has_multiple_frames = len(m) > 1
|
||||||
@ -202,7 +204,15 @@ def render_image(
|
|||||||
if exe is None:
|
if exe is None:
|
||||||
raise OSError('Failed to find the ImageMagick convert executable, make sure it is present in PATH')
|
raise OSError('Failed to find the ImageMagick convert executable, make sure it is present in PATH')
|
||||||
cmd = [exe]
|
cmd = [exe]
|
||||||
cmd += ['-background', 'none', '--', path]
|
if remove_alpha:
|
||||||
|
cmd += ['-background', remove_alpha, '-alpha', 'remove']
|
||||||
|
else:
|
||||||
|
cmd += ['-background', 'none']
|
||||||
|
if flip:
|
||||||
|
cmd.append('-flip')
|
||||||
|
if flop:
|
||||||
|
cmd.append('-flop')
|
||||||
|
cmd += ['--', path]
|
||||||
if only_first_frame and has_multiple_frames:
|
if only_first_frame and has_multiple_frames:
|
||||||
cmd[-1] += '[0]'
|
cmd[-1] += '[0]'
|
||||||
cmd.append('-auto-orient')
|
cmd.append('-auto-orient')
|
||||||
@ -287,12 +297,15 @@ def render_as_single_image(
|
|||||||
path: str, m: ImageData,
|
path: str, m: ImageData,
|
||||||
available_width: int, available_height: int,
|
available_width: int, available_height: int,
|
||||||
scale_up: bool,
|
scale_up: bool,
|
||||||
tdir: Optional[str] = None
|
tdir: Optional[str] = None,
|
||||||
|
remove_alpha: str = '', flip: bool = False, flop: bool = False,
|
||||||
) -> Tuple[str, int, int]:
|
) -> Tuple[str, int, int]:
|
||||||
import tempfile
|
import tempfile
|
||||||
fd, output = tempfile.mkstemp(prefix='icat-', suffix=f'.{m.mode}', dir=tdir)
|
fd, output = tempfile.mkstemp(prefix='icat-', suffix=f'.{m.mode}', dir=tdir)
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
result = render_image(path, output, m, available_width, available_height, scale_up, only_first_frame=True)
|
result = render_image(
|
||||||
|
path, output, m, available_width, available_height, scale_up,
|
||||||
|
only_first_frame=True, remove_alpha=remove_alpha, flip=flip, flop=flop)
|
||||||
os.rename(result.frames[0].path, output)
|
os.rename(result.frames[0].path, output)
|
||||||
return output, result.width, result.height
|
return output, result.width, result.height
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,7 @@ __all__ = (
|
|||||||
'GraphicsCommandType', 'HandlerType', 'AbstractEventLoop', 'AddressFamily', 'Socket', 'CompletedProcess',
|
'GraphicsCommandType', 'HandlerType', 'AbstractEventLoop', 'AddressFamily', 'Socket', 'CompletedProcess',
|
||||||
'PopenType', 'Protocol', 'TypedDict', 'MarkType', 'ImageManagerType', 'Debug', 'LoopType', 'MouseEvent',
|
'PopenType', 'Protocol', 'TypedDict', 'MarkType', 'ImageManagerType', 'Debug', 'LoopType', 'MouseEvent',
|
||||||
'TermManagerType', 'BossType', 'ChildType', 'BadLineType', 'MouseButton',
|
'TermManagerType', 'BossType', 'ChildType', 'BadLineType', 'MouseButton',
|
||||||
'KeyActionType', 'KeyMap', 'KittyCommonOpts', 'SequenceMap', 'CoreTextFont', 'WindowSystemMouseEvent',
|
'KeyActionType', 'KeyMap', 'KittyCommonOpts', 'AliasMap', 'SequenceMap', 'CoreTextFont', 'WindowSystemMouseEvent',
|
||||||
'FontConfigPattern', 'ScreenType', 'StartupCtx', 'KeyEventType', 'LayoutType', 'PowerlineStyle',
|
'FontConfigPattern', 'ScreenType', 'StartupCtx', 'KeyEventType', 'LayoutType', 'PowerlineStyle',
|
||||||
'RemoteCommandType', 'SessionType', 'SessionTab', 'SpecialWindowInstance', 'TabType', 'ScreenSize', 'WindowType'
|
'RemoteCommandType', 'SessionType', 'SessionTab', 'SpecialWindowInstance', 'TabType', 'ScreenSize', 'WindowType'
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user