Allow controlling the number of loops in icat
This commit is contained in:
parent
8033e9fef5
commit
b479510f6c
@ -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.
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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),
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user