Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support GIF image decoder and animation #40

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)

jserv marked this conversation as resolved.
Show resolved Hide resolved
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 =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid specifying window title to the full path of given GIF file. Instead, use fixed names such as "Viewer."

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;
jserv marked this conversation as resolved.
Show resolved Hide resolved
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