Skip to content

Commit

Permalink
Extend twin_pixmap_t to Support gif animation
Browse files Browse the repository at this point in the history
To enable support for GIF animations, I have extended the twin_pixmap_t
structure. The twin_pixmap_t now includes a new pointer to the new
twin_animation_t structure, which manages multiple frames and their
timing information for animated image.

To manipulate twin_animation_t, the following APIs have been added.
- twin_animation_get_current_delay : Retrieves the display duration of
the current frame.
- twin_animation_get_current_frame : Obtains the current frame for
display.
- twin_animation_advance_frame : Advances the animation to the next
frame. If the animation is looping, it will return to the first frame
after the last one.
- twin_animation_destroy : Frees the memory allocated for the animation,
including all associated frames.

To show the new features, a demo application has been developed. This
application supports both animated images and static images, and serves
as a practical example of how to utilize the new animation capabilities.
  • Loading branch information
ndsl7109256 committed Sep 11, 2024
1 parent 1ab586c commit 241ecd1
Show file tree
Hide file tree
Showing 12 changed files with 919 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ libtwin.a_files-y = \
src/pixmap.c \
src/timeout.c \
src/image.c \
src/animation.c \
src/api.c

libtwin.a_includes-y := \
Expand All @@ -74,6 +75,10 @@ libtwin.a_cflags-y += $(shell pkg-config --cflags libpng)
TARGET_LIBS += $(shell pkg-config --libs libpng)
endif

ifeq ($(CONFIG_LOADER_GIF), y)
libtwin.a_files-y += src/image-gif.c
endif

# Applications

libapps.a_files-y := apps/dummy.c
Expand All @@ -83,6 +88,7 @@ libapps.a_files-$(CONFIG_DEMO_CLOCK) += apps/clock.c
libapps.a_files-$(CONFIG_DEMO_CALCULATOR) += apps/calc.c
libapps.a_files-$(CONFIG_DEMO_LINE) += apps/line.c
libapps.a_files-$(CONFIG_DEMO_SPLINE) += apps/spline.c
libapps.a_files-$(CONFIG_DEMO_ANIMATION) += apps/animation.c

libapps.a_includes-y := include

Expand Down
105 changes: 105 additions & 0 deletions apps/animation.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2024 National Cheng Kung University
* All rights reserved.
*/

#include <stdlib.h>

#include "twin_private.h"

#include "apps_animation.h"

#define _apps_animation_pixmap(animation) ((animation)->widget.window->pixmap)

typedef struct {
twin_widget_t widget;
twin_pixmap_t *pix;
twin_timeout_t *timeout;
} apps_animation_t;

static void _apps_animation_paint(apps_animation_t *anim)
{
twin_pixmap_t *current_frame = NULL;

if (twin_pixmap_is_animated(anim->pix)) {
twin_animation_t *a = anim->pix->animation;
current_frame = twin_animation_get_current_frame(a);
twin_animation_advance_frame(a);
} else {
current_frame = anim->pix;
}

twin_operand_t srcop = {
.source_kind = TWIN_PIXMAP,
.u.pixmap = current_frame,
};
twin_composite(_apps_animation_pixmap(anim), 0, 0, &srcop, 0, 0, NULL, 0, 0,
TWIN_SOURCE, current_frame->width, current_frame->height);
}

static twin_time_t _apps_animation_timeout(twin_time_t maybe_unused now,
void *closure)
{
apps_animation_t *anim = closure;
_twin_widget_queue_paint(&anim->widget);
twin_animation_t *a = anim->pix->animation;
twin_time_t delay = twin_animation_get_current_delay(a);
return delay;
}

static twin_dispatch_result_t _apps_animation_dispatch(twin_widget_t *widget,
twin_event_t *event)
{
apps_animation_t *anim = (apps_animation_t *) widget;
if (_twin_widget_dispatch(widget, event) == TwinDispatchDone)
return TwinDispatchDone;
switch (event->kind) {
case TwinEventPaint:
_apps_animation_paint(anim);
break;
default:
break;
}
return TwinDispatchContinue;
}

static void _apps_animation_init(apps_animation_t *anim,
twin_box_t *parent,
twin_dispatch_proc_t dispatch)
{
static const twin_widget_layout_t preferred = {0, 0, 1, 1};
_twin_widget_init(&anim->widget, parent, 0, preferred, dispatch);

if (twin_pixmap_is_animated(anim->pix)) {
twin_animation_t *a = anim->pix->animation;
twin_time_t delay = twin_animation_get_current_delay(a);
anim->timeout = twin_set_timeout(_apps_animation_timeout, delay, anim);
} else {
anim->timeout = NULL;
}
}

static apps_animation_t *apps_animation_create(twin_box_t *parent,
twin_pixmap_t *pix)
{
apps_animation_t *anim = malloc(sizeof(apps_animation_t));
anim->pix = pix;
_apps_animation_init(anim, parent, _apps_animation_dispatch);
return anim;
}

void apps_animation_start(twin_screen_t *screen,
const char *name,
const char *path,
int x,
int y)
{
twin_pixmap_t *pix = twin_pixmap_from_file(path, TWIN_ARGB32);
twin_toplevel_t *toplevel =
twin_toplevel_create(screen, TWIN_ARGB32, TwinWindowApplication, x, y,
pix->width, pix->height, name);
apps_animation_t *anim = apps_animation_create(&toplevel->box, pix);
(void) anim;
twin_toplevel_show(toplevel);
}
18 changes: 18 additions & 0 deletions apps/apps_animation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2024 National Cheng Kung University
* All rights reserved.
*/

#ifndef _APPS_ANIMATION_H_
#define _APPS_ANIMATION_H_

#include <twin.h>

void apps_animation_start(twin_screen_t *screen,
const char *name,
const char *path,
int x,
int y);

#endif /* _APPS_ANIMATION_H_ */
5 changes: 5 additions & 0 deletions apps/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <time.h>
#include <unistd.h>

#include "apps_animation.h"
#include "apps_calc.h"
#include "apps_clock.h"
#include "apps_hello.h"
Expand Down Expand Up @@ -122,6 +123,10 @@ int main(void)
#if defined(CONFIG_DEMO_SPLINE)
apps_spline_start(tx->screen, "Spline", 20, 20, 400, 400);
#endif
#if defined(CONFIG_DEMO_ANIMATION)
apps_animation_start(tx->screen, "Viewer", ASSET_PATH "nyancat.gif", 20,
20);
#endif

twin_dispatch();

Expand Down
Binary file added assets/nyancat.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ config LOADER_JPEG
bool "Enable JPEG loader"
default y

config LOADER_GIF
bool "Enable GIF loader"
default y

endmenu

menu "Demo Applications"
Expand Down Expand Up @@ -72,4 +76,8 @@ config DEMO_SPLINE
default y
depends on DEMO_APPLICATIONS

config DEMO_ANIMATION
bool "Build animation demo"
default y
depends on DEMO_APPLICATIONS
endmenu
2 changes: 2 additions & 0 deletions configs/defconfig
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
CONFIG_BACKEND_SDL=y
CONFIG_LOADER_PNG=y
CONFIG_LOADER_JPEG=y
CONFIG_LOADER_GIF=y
CONFIG_DEMO_APPLICATIONS=y
CONFIG_DEMO_MULTI=y
CONFIG_DEMO_HELLO=y
CONFIG_DEMO_CLOCK=y
CONFIG_DEMO_CALCULATOR=y
CONFIG_DEMO_LINE=y
CONFIG_DEMO_SPLINE=y
CONFIG_DEMO_ANIMATION=y
52 changes: 52 additions & 0 deletions include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ typedef union _twin_pointer {

typedef struct _twin_window twin_window_t;
typedef struct _twin_screen twin_screen_t;
typedef struct _twin_pixmap twin_pixmap_t;
typedef struct _twin_animation twin_animation_t;

/*
* Events
Expand Down Expand Up @@ -133,6 +135,27 @@ typedef struct _twin_event_queue {
twin_event_t event;
} twin_event_queue_t;

typedef struct _twin_animation_iter {
twin_animation_t *anim;
twin_count_t current_index;
twin_pixmap_t *current_frame;
twin_time_t current_delay;
} twin_animation_iter_t;

typedef struct _twin_animation {
/* Array of pixmaps representing each frame of the animation */
twin_pixmap_t **frames;
/* Number of frames in the animation */
twin_count_t n_frames;
/* Delay between frames in milliseconds */
twin_time_t *frame_delays;
/* Whether the animation should loop */
bool loop;
twin_animation_iter_t *iter;
twin_coord_t width; /* pixels */
twin_coord_t height; /* pixels */
} twin_animation_t;

/*
* A rectangular array of pixels
*/
Expand Down Expand Up @@ -171,6 +194,7 @@ typedef struct _twin_pixmap {
/*
* Pixels
*/
twin_animation_t *animation;
twin_pointer_t p;
/*
* When representing a window, this point
Expand Down Expand Up @@ -692,6 +716,32 @@ void twin_icon_draw(twin_pixmap_t *pixmap,

twin_pixmap_t *twin_pixmap_from_file(const char *path, twin_format_t fmt);

/*
* animation.c
*
* Defines the interface for managing frame-based animations.
* It provides functions to control and manipulate animations such as getting
* the current frame, advancing the animation, and releasing resources.
*/

/* Get the number of milliseconds the current frame should be displayed. */
twin_time_t twin_animation_get_current_delay(const twin_animation_t *anim);

/* Get the current frame which should be displayed. */
twin_pixmap_t *twin_animation_get_current_frame(const twin_animation_t *anim);

/* Advances the animation to the next frame. If the animation is looping, it
* will return to the first frame after the last one. */
void twin_animation_advance_frame(twin_animation_t *anim);

/* Frees the memory allocated for the animation, including all associated
* frames. */
void twin_animation_destroy(twin_animation_t *anim);

twin_animation_iter_t *twin_animation_iter_init(twin_animation_t *anim);

void twin_animation_iter_advance(twin_animation_iter_t *iter);

/*
* label.c
*/
Expand Down Expand Up @@ -856,6 +906,8 @@ twin_pixmap_t *twin_make_pattern(void);
* pixmap.c
*/

#define twin_pixmap_is_animated(pix) ((pix)->animation != NULL)

twin_pixmap_t *twin_pixmap_create(twin_format_t format,
twin_coord_t width,
twin_coord_t height);
Expand Down
71 changes: 71 additions & 0 deletions src/animation.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2024 National Cheng Kung University, Taiwan
* All rights reserved.
*/
#include <stdlib.h>

#include "twin.h"

twin_time_t twin_animation_get_current_delay(const twin_animation_t *anim)
{
if (!anim)
return 0;
return anim->iter->current_delay;
}

twin_pixmap_t *twin_animation_get_current_frame(const twin_animation_t *anim)
{
if (!anim)
return NULL;
return anim->iter->current_frame;
}

void twin_animation_advance_frame(twin_animation_t *anim)
{
if (!anim)
return;
twin_animation_iter_advance(anim->iter);
}

void twin_animation_destroy(twin_animation_t *anim)
{
if (!anim)
return;

free(anim->iter);
for (twin_count_t i = 0; i < anim->n_frames; i++) {
twin_pixmap_destroy(anim->frames[i]);
}
free(anim->frames);
free(anim->frame_delays);
free(anim);
}

twin_animation_iter_t *twin_animation_iter_init(twin_animation_t *anim)
{
twin_animation_iter_t *iter = malloc(sizeof(twin_animation_iter_t));
if (!iter || !anim)
return NULL;
iter->current_index = 0;
iter->current_frame = anim->frames[0];
iter->current_delay = anim->frame_delays[0];
anim->iter = iter;
iter->anim = anim;
return iter;
}

void twin_animation_iter_advance(twin_animation_iter_t *iter)
{
twin_animation_t *anim = iter->anim;
iter->current_index++;
if (iter->current_index >= anim->n_frames) {
if (anim->loop) {
iter->current_index = 0;
} else {
iter->current_index = anim->n_frames - 1;
}
}
iter->current_frame = anim->frames[iter->current_index];
iter->current_delay = anim->frame_delays[iter->current_index];
}
Loading

0 comments on commit 241ecd1

Please sign in to comment.