Allow controlling the number of loops in icat

This commit is contained in:
Kovid Goyal 2021-02-04 17:47:53 +05:30
parent 8033e9fef5
commit b479510f6c
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 50 additions and 22 deletions

View File

@ -563,7 +563,7 @@ Key Value Default Description
**Keys for animation control**
-----------------------------------------------------------
``s`` Positive integer ``0`` ``1`` - start animation, ``>1`` - stop animation
``s`` Positive integer ``0`` ``1`` - stop animation, ``2`` - run animation, but wait for new frames, ``3`` - run animation
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being affected
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the next one. A value of
zero is ignored. Negative values create a *gapless* frame.

View File

@ -102,7 +102,7 @@ but you can turn it off or on explicitly, if needed.
--silent
type=bool-set
Do not print out anything to stdout during operation.
Do not print out anything to STDOUT during operation.
--z-index -z
@ -112,9 +112,17 @@ a double minus for values under the threshold for drawing images under cell back
colors. For example, --1 evaluates as -1,073,741,825.
--loop -l
default=-1
type=int
Number of times to loop animations. Negative values loop forever. Zero means
only the first frame of the animation is displayed. Otherwise, the animation
is looped the specified number of times.
--hold
type=bool-set
Wait for keypress before exiting after displaying the images.
Wait for a key press before exiting after displaying the images.
'''
@ -236,7 +244,7 @@ def show(
write_chunked(cmd, data)
def show_frames(frame_data: RenderedImage, use_number: int) -> None:
def show_frames(frame_data: RenderedImage, use_number: int, loops: int) -> None:
transmit_cmd = GraphicsCommand()
transmit_cmd.a = 'f'
transmit_cmd.I = use_number # noqa
@ -245,7 +253,7 @@ def show_frames(frame_data: RenderedImage, use_number: int) -> None:
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:
def control(frame_number: int = 0, loops: Optional[int] = None, gap: Optional[int] = 0, animation_control: int = 0) -> None:
cmd = GraphicsCommand()
cmd.a = 'a'
cmd.I = use_number # noqa
@ -254,8 +262,8 @@ def show_frames(frame_data: RenderedImage, use_number: int) -> None:
cmd.v = loops + 1
if gap is not None:
cmd.z = gap if gap > 0 else -1
if start_animation:
cmd.s = 1
if animation_control:
cmd.s = animation_control
write_gr_cmd(cmd)
anchor_frame = 0
@ -265,7 +273,7 @@ def show_frames(frame_data: RenderedImage, use_number: int) -> None:
if frame.dispose < Dispose.previous:
anchor_frame = frame_number
if frame_number == 1:
control(frame_number, gap=frame.gap, loops=1)
control(frame_number, gap=frame.gap, loops=None if loops < 1 else loops)
continue
if frame.dispose is Dispose.previous:
if anchor_frame != frame_number:
@ -283,7 +291,9 @@ def show_frames(frame_data: RenderedImage, use_number: int) -> None:
with open(frame.path, 'rb') as f:
data = f.read()
write_chunked(transmit_cmd, data)
control(loops=0, start_animation=True)
if frame_number == 2:
control(animation_control=2)
control(animation_control=3)
def parse_z_index(val: str) -> int:
@ -318,7 +328,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
else:
fmt = 24 if m.mode == 'rgb' else 32
transmit_mode = 't'
if len(m) == 1:
if len(m) == 1 or args.loop == 0:
outfile, width, height = render_as_single_image(path, m, available_width, available_height, args.scale_up)
else:
import struct
@ -332,7 +342,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
align=args.align, place=parsed_opts.place, use_number=use_number
)
if use_number:
show_frames(frame_data, use_number)
show_frames(frame_data, use_number, args.loop)
if not can_transfer_with_files:
for fr in frame_data.frames:
with contextlib.suppress(FileNotFoundError):

View File

@ -776,7 +776,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
rd->texture_id = img->texture_id;
img->is_drawn = true;
}
if (img->is_drawn && !was_drawn && img->animation_enabled && img->extra_framecnt && img->animation_duration) {
if (img->is_drawn && !was_drawn && img->animation_state != ANIMATION_STOPPED && img->extra_framecnt && img->animation_duration) {
self->has_images_needing_animation = true;
global_state.check_for_active_animated_images = true;
}
@ -806,7 +806,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
#define _frame_number num_lines
#define _other_frame_number num_cells
#define _gap z_index
#define _animation_enabled data_width
#define _animation_state data_width
#define _blend_mode cell_x_offset
#define _bgcolor cell_y_offset
#define _loop_count data_height
@ -1102,6 +1102,10 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
ABRT("ENOSPC", "Failed to cache data for image frame");
}
img->animation_duration += frame->gap;
if (img->animation_state == ANIMATION_LOADING) {
self->has_images_needing_animation = true;
global_state.check_for_active_animated_images = true;
}
} else {
frame = frame_for_number(img, frame_number);
if (!frame) ABRT("EINVAL", "No frame with number: %u found", frame_number);
@ -1199,11 +1203,22 @@ handle_animation_control_command(GraphicsManager *self, bool *is_dirty, const Gr
update_current_frame(self, img, NULL);
}
}
if (g->_animation_enabled) {
bool was_enabled = img->animation_enabled;
img->animation_enabled = g->_animation_enabled == 1;
if (img->animation_enabled) {
if (!was_enabled) img->current_frame_shown_at = monotonic();
if (g->_animation_state) {
AnimationState old_state = img->animation_state;
switch(g->_animation_state) {
case 1:
img->animation_state = ANIMATION_STOPPED; break;
case 2:
img->animation_state = ANIMATION_LOADING; break;
case 3:
img->animation_state = ANIMATION_RUNNING; break;
default:
break;
}
if (img->animation_state == ANIMATION_STOPPED) {
img->current_loop = 0;
} else {
if (old_state == ANIMATION_STOPPED) img->current_frame_shown_at = monotonic();
self->has_images_needing_animation = true;
global_state.check_for_active_animated_images = true;
}
@ -1217,7 +1232,7 @@ handle_animation_control_command(GraphicsManager *self, bool *is_dirty, const Gr
static inline bool
image_is_animatable(const Image *img) {
return img->animation_enabled && img->extra_framecnt && img->is_drawn && img->animation_duration && (
return img->animation_state != ANIMATION_STOPPED && img->extra_framecnt && img->is_drawn && img->animation_duration && (
!img->max_loops || img->current_loop < img->max_loops);
}
@ -1239,6 +1254,7 @@ scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t
do {
uint32_t next = (img->current_frame_index + 1) % (img->extra_framecnt + 1);
if (!next) {
if (img->animation_state == ANIMATION_LOADING) goto skip_image;
if (++img->current_loop >= img->max_loops && img->max_loops) goto skip_image;
}
img->current_frame_index = next;
@ -1571,10 +1587,10 @@ image_as_dict(GraphicsManager *self, Image *img) {
}
CoalescedFrameData cfd = get_coalesced_frame_data(self, img, &img->root_frame);
if (!cfd.buf) { PyErr_SetString(PyExc_RuntimeError, "Failed to get data for root frame"); return NULL; }
PyObject *ans = Py_BuildValue("{sI sI sI sI sK sI sI " "sO sO sO " "sI sI sI " "sI sy# sN}",
PyObject *ans = Py_BuildValue("{sI sI sI sI sK sI sI " "sO sI sO " "sI sI sI " "sI sy# sN}",
U(texture_id), U(client_id), U(width), U(height), U(internal_id), U(refcnt), U(client_number),
B(data_loaded), B(animation_enabled), "is_4byte_aligned", img->root_frame.is_4byte_aligned ? Py_True : Py_False,
B(data_loaded), U(animation_state), "is_4byte_aligned", img->root_frame.is_4byte_aligned ? Py_True : Py_False,
U(current_frame_index), "root_frame_gap", img->root_frame.gap, U(current_frame_index),

View File

@ -34,6 +34,7 @@ typedef struct {
bool is_opaque, is_4byte_aligned, alpha_blend;
} Frame;
typedef enum { ANIMATION_STOPPED = 0, ANIMATION_LOADING = 1, ANIMATION_RUNNING = 2} AnimationState;
typedef struct {
uint32_t texture_id, client_id, client_number, width, height;
@ -47,7 +48,8 @@ typedef struct {
size_t refcnt, refcap, extra_framecnt;
monotonic_t atime;
size_t used_storage;
bool animation_enabled, is_drawn;
bool is_drawn;
AnimationState animation_state;
uint32_t max_loops, current_loop;
monotonic_t current_frame_shown_at;
} Image;