Allow gapless frames
This commit is contained in:
parent
9cf5348c36
commit
bb5c5a8e4f
@ -551,15 +551,16 @@ Key Value Default Description
|
|||||||
``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the base data
|
``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the base data
|
||||||
when creating a new frame, by default the base data is black, fully transparent pixels
|
when creating a new frame, by default the base data is black, fully transparent pixels
|
||||||
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being edited. By default, a new frame is created
|
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being edited. By default, a new frame is created
|
||||||
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the previous one. Values less than
|
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the previous one. A value of
|
||||||
one are ignored, new frames are given a default gap of ``40ms`` if not specified.
|
zero is ignored. Negative values create a *gapless* frame. If not specified,
|
||||||
|
frames have a default gap of ``40ms``. The root frame defaults to zero gap.
|
||||||
|
|
||||||
**Keys for animation control**
|
**Keys for animation control**
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
``s`` Positive integer ``0`` ``1`` - start animation, ``>1`` - stop animation
|
``s`` Positive integer ``0`` ``1`` - start animation, ``>1`` - stop animation
|
||||||
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being affected
|
``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 previous one. Values less than
|
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the previous one. A value of
|
||||||
one are ignored
|
zero is ignored. Negative values create a *gapless* frame.
|
||||||
``c`` Positive integer ``0`` The 1-based frame number of the frame that should be made the current frame
|
``c`` Positive integer ``0`` The 1-based frame number of the frame that should be made the current frame
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -766,7 +766,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
|
|||||||
rd->texture_id = img->texture_id;
|
rd->texture_id = img->texture_id;
|
||||||
img->is_drawn = true;
|
img->is_drawn = true;
|
||||||
}
|
}
|
||||||
if (img->is_drawn && !was_drawn && img->animation_enabled && img->extra_framecnt) {
|
if (img->is_drawn && !was_drawn && img->animation_enabled && img->extra_framecnt && img->animation_duration) {
|
||||||
self->has_images_needing_animation = true;
|
self->has_images_needing_animation = true;
|
||||||
global_state.has_active_animated_images = true;
|
global_state.has_active_animated_images = true;
|
||||||
}
|
}
|
||||||
@ -821,6 +821,14 @@ update_current_frame(GraphicsManager *self, Image *img, void *data) {
|
|||||||
img->current_frame_shown_at = monotonic();
|
img->current_frame_shown_at = monotonic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
change_gap(Image *img, Frame *f, int32_t gap) {
|
||||||
|
uint32_t prev_gap = f->gap;
|
||||||
|
f->gap = MAX(0, gap);
|
||||||
|
img->animation_duration = prev_gap < img->animation_duration ? img->animation_duration - prev_gap : 0;
|
||||||
|
img->animation_duration += f->gap;
|
||||||
|
}
|
||||||
|
|
||||||
static Image*
|
static Image*
|
||||||
handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, Image *img, const uint8_t *payload, bool *is_dirty) {
|
handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, Image *img, const uint8_t *payload, bool *is_dirty) {
|
||||||
uint32_t frame_number = g->_frame_number, fmt = g->format ? g->format : RGBA;
|
uint32_t frame_number = g->_frame_number, fmt = g->format ? g->format : RGBA;
|
||||||
@ -925,15 +933,15 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I
|
|||||||
ABRT("ENOSPC", "Failed to cache data for image frame");
|
ABRT("ENOSPC", "Failed to cache data for image frame");
|
||||||
}
|
}
|
||||||
if (is_new_frame) {
|
if (is_new_frame) {
|
||||||
if (!img->extra_framecnt) img->root_frame.gap = g->_gap > 0 ? g->_gap : DEFAULT_GAP;
|
|
||||||
Frame *frames = realloc(img->extra_frames, sizeof(img->extra_frames[0]) * img->extra_framecnt + 1);
|
Frame *frames = realloc(img->extra_frames, sizeof(img->extra_frames[0]) * img->extra_framecnt + 1);
|
||||||
if (!frames) ABRT("ENOMEM", "Out of memory");
|
if (!frames) ABRT("ENOMEM", "Out of memory");
|
||||||
img->extra_frames = frames;
|
img->extra_frames = frames;
|
||||||
img->extra_framecnt++;
|
img->extra_framecnt++;
|
||||||
img->extra_frames[frame_number - 2].gap = DEFAULT_GAP;
|
img->extra_frames[frame_number - 2].gap = DEFAULT_GAP;
|
||||||
img->extra_frames[frame_number - 2].id = key.frame_id;
|
img->extra_frames[frame_number - 2].id = key.frame_id;
|
||||||
|
img->animation_duration += DEFAULT_GAP;
|
||||||
}
|
}
|
||||||
if (g->_gap > 0) img->extra_frames[frame_number - 2].gap = g->_gap;
|
if (g->_gap > 0) change_gap(img, img->extra_frames + frame_number - 2, g->_gap);
|
||||||
}
|
}
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
@ -957,17 +965,21 @@ handle_delete_frame_command(GraphicsManager *self, const GraphicsCommand *g, boo
|
|||||||
*is_dirty = true;
|
*is_dirty = true;
|
||||||
ImageAndFrame key = {.image_id=img->internal_id};
|
ImageAndFrame key = {.image_id=img->internal_id};
|
||||||
bool remove_root = frame_number == 1;
|
bool remove_root = frame_number == 1;
|
||||||
|
uint32_t removed_gap = 0;
|
||||||
if (remove_root) {
|
if (remove_root) {
|
||||||
key.frame_id = img->root_frame.id;
|
key.frame_id = img->root_frame.id;
|
||||||
remove_from_cache(self, key);
|
remove_from_cache(self, key);
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
|
removed_gap = img->root_frame.gap;
|
||||||
img->root_frame = img->extra_frames[0];
|
img->root_frame = img->extra_frames[0];
|
||||||
}
|
}
|
||||||
unsigned removed_idx = remove_root ? 0 : frame_number - 2;
|
unsigned removed_idx = remove_root ? 0 : frame_number - 2;
|
||||||
if (!remove_root) {
|
if (!remove_root) {
|
||||||
key.frame_id = img->extra_frames[removed_idx].id;
|
key.frame_id = img->extra_frames[removed_idx].id;
|
||||||
|
removed_gap = img->extra_frames[removed_idx].gap;
|
||||||
remove_from_cache(self, key);
|
remove_from_cache(self, key);
|
||||||
}
|
}
|
||||||
|
img->animation_duration = removed_gap < img->animation_duration ? img->animation_duration - removed_gap : 0;
|
||||||
if (PyErr_Occurred()) PyErr_Print();
|
if (PyErr_Occurred()) PyErr_Print();
|
||||||
if (removed_idx < img->extra_framecnt - 1) memmove(img->extra_frames + removed_idx, img->extra_frames + removed_idx + 1, sizeof(img->extra_frames[0]) * img->extra_framecnt - 1 - removed_idx);
|
if (removed_idx < img->extra_framecnt - 1) memmove(img->extra_frames + removed_idx, img->extra_frames + removed_idx + 1, sizeof(img->extra_frames[0]) * img->extra_framecnt - 1 - removed_idx);
|
||||||
img->extra_framecnt--;
|
img->extra_framecnt--;
|
||||||
@ -987,7 +999,7 @@ handle_animation_control_command(GraphicsManager *self, bool *is_dirty, const Gr
|
|||||||
uint32_t frame_idx = g->_frame_number - 1;
|
uint32_t frame_idx = g->_frame_number - 1;
|
||||||
if (frame_idx <= img->extra_framecnt) {
|
if (frame_idx <= img->extra_framecnt) {
|
||||||
Frame *f = frame_idx ? img->extra_frames + frame_idx - 1 : &img->root_frame;
|
Frame *f = frame_idx ? img->extra_frames + frame_idx - 1 : &img->root_frame;
|
||||||
if (g->_gap > 0) f->gap = g->_gap;
|
if (g->_gap) change_gap(img, f, g->_gap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (g->_other_frame_number) {
|
if (g->_other_frame_number) {
|
||||||
@ -1018,13 +1030,15 @@ scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t
|
|||||||
self->context_made_current_for_this_command = true;
|
self->context_made_current_for_this_command = true;
|
||||||
for (size_t i = self->image_count; i-- > 0;) {
|
for (size_t i = self->image_count; i-- > 0;) {
|
||||||
Image *img = self->images + i;
|
Image *img = self->images + i;
|
||||||
if (img->animation_enabled && img->extra_framecnt && img->is_drawn) {
|
if (img->animation_enabled && img->extra_framecnt && img->is_drawn && img->animation_duration) {
|
||||||
self->has_images_needing_animation = true;
|
self->has_images_needing_animation = true;
|
||||||
Frame *next_frame = img->current_frame_index + 1 < img->extra_framecnt ? img->extra_frames + img->current_frame_index + 1 : &img->root_frame;
|
Frame *next_frame = img->current_frame_index + 1 < img->extra_framecnt ? img->extra_frames + img->current_frame_index + 1 : &img->root_frame;
|
||||||
monotonic_t next_frame_at = img->current_frame_shown_at + ms_to_monotonic_t(next_frame->gap);
|
monotonic_t next_frame_at = img->current_frame_shown_at + ms_to_monotonic_t(next_frame->gap);
|
||||||
if (now >= next_frame_at) {
|
if (now >= next_frame_at) {
|
||||||
dirtied = true;
|
dirtied = true;
|
||||||
|
do {
|
||||||
img->current_frame_index = (img->current_frame_index + 1) % (img->extra_framecnt + 1);
|
img->current_frame_index = (img->current_frame_index + 1) % (img->extra_framecnt + 1);
|
||||||
|
} while(!current_frame(img)->gap);
|
||||||
update_current_frame(self, img, NULL);
|
update_current_frame(self, img, NULL);
|
||||||
next_frame = img->current_frame_index + 1 < img->extra_framecnt ? img->extra_frames + img->current_frame_index + 1 : &img->root_frame;
|
next_frame = img->current_frame_index + 1 < img->extra_framecnt ? img->extra_frames + img->current_frame_index + 1 : &img->root_frame;
|
||||||
}
|
}
|
||||||
@ -1341,10 +1355,11 @@ image_as_dict(GraphicsManager *self, Image *img) {
|
|||||||
if (PyErr_Occurred()) { Py_CLEAR(frames); return NULL; }
|
if (PyErr_Occurred()) { Py_CLEAR(frames); return NULL; }
|
||||||
}
|
}
|
||||||
key.frame_id = img->root_frame.id;
|
key.frame_id = img->root_frame.id;
|
||||||
return Py_BuildValue("{sI sI sI sI sK sI sI sO sO sO sI sI sI sN sN}",
|
return Py_BuildValue("{sI sI sI sI sK sI sI sO sO sO sI sI sI sI sN sN}",
|
||||||
U(texture_id), U(client_id), U(width), U(height), U(internal_id), U(refcnt), U(client_number),
|
U(texture_id), U(client_id), U(width), U(height), U(internal_id), U(refcnt), U(client_number),
|
||||||
B(data_loaded), B(is_4byte_aligned), B(animation_enabled),
|
B(data_loaded), B(is_4byte_aligned), B(animation_enabled),
|
||||||
U(current_frame_index), "root_frame_gap", img->root_frame.gap, U(current_frame_index),
|
U(current_frame_index), "root_frame_gap", img->root_frame.gap, U(current_frame_index),
|
||||||
|
U(animation_duration),
|
||||||
"data", read_from_cache_python(self, key), "extra_frames", frames
|
"data", read_from_cache_python(self, key), "extra_frames", frames
|
||||||
);
|
);
|
||||||
#undef B
|
#undef B
|
||||||
|
|||||||
@ -57,6 +57,7 @@ typedef struct {
|
|||||||
ImageRef *refs;
|
ImageRef *refs;
|
||||||
Frame *extra_frames, root_frame;
|
Frame *extra_frames, root_frame;
|
||||||
uint32_t current_frame_index, frame_id_counter;
|
uint32_t current_frame_index, frame_id_counter;
|
||||||
|
uint64_t animation_duration;
|
||||||
size_t refcnt, refcap, extra_framecnt;
|
size_t refcnt, refcap, extra_framecnt;
|
||||||
monotonic_t atime;
|
monotonic_t atime;
|
||||||
size_t used_storage;
|
size_t used_storage;
|
||||||
|
|||||||
@ -638,7 +638,7 @@ class TestGraphics(BaseTest):
|
|||||||
))
|
))
|
||||||
# test changing gaps
|
# test changing gaps
|
||||||
img = g.image_for_client_id(1)
|
img = g.image_for_client_id(1)
|
||||||
self.assertEqual(img['root_frame_gap'], 77)
|
self.assertEqual(img['root_frame_gap'], 0)
|
||||||
self.assertIsNone(li(a='a', i=1, r=1, z=13))
|
self.assertIsNone(li(a='a', i=1, r=1, z=13))
|
||||||
img = g.image_for_client_id(1)
|
img = g.image_for_client_id(1)
|
||||||
self.assertEqual(img['root_frame_gap'], 13)
|
self.assertEqual(img['root_frame_gap'], 13)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user