Skip to content

Commit

Permalink
Support gif animation
Browse files Browse the repository at this point in the history
To enable support for animations for GIF. I add new animation structure
twin_animation_t to manage the frames and the timing infomation and new
API functions using gifdec[1] to load, manage, and display animations.
Besides, I add a new animation app to demonstrate GIF loading and
display.

[1] https://github.com/lecram/gifdec

Close #37
  • Loading branch information
ndsl7109256 committed Aug 29, 2024
1 parent 1ab586c commit 37af53a
Show file tree
Hide file tree
Showing 11 changed files with 846 additions and 1 deletion.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ libtwin.a_files-y = \
src/pixmap.c \
src/timeout.c \
src/image.c \
src/api.c
src/api.c \
src/gifdec.c\
src/gif.c

libtwin.a_includes-y := \
include \
Expand Down Expand Up @@ -83,6 +85,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
95 changes: 95 additions & 0 deletions apps/animation.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2004 Keith Packard <[email protected]>
* All rights reserved.
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.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_animation_t *gif;
twin_timeout_t *timeout;
} apps_animation_t;

static void _apps_animation_paint(apps_animation_t *animation)
{
twin_pixmap_t *current_frame =
twin_animation_get_current_frame(animation->gif);

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

twin_animation_advance_frame(animation->gif);
}

static twin_time_t _apps_animation_timeout(twin_time_t maybe_unused now,
void *closure)
{
apps_animation_t *animation = closure;
_twin_widget_queue_paint(&animation->widget);
twin_time_t delay = animation->gif->frame_delays[animation->gif->current_frame];
return delay;
}

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

static void _apps_animation_init(apps_animation_t *animation,
twin_box_t *parent,
twin_dispatch_proc_t dispatch)
{
static const twin_widget_layout_t preferred = {0, 0, 1, 1};
_twin_widget_init(&animation->widget, parent, 0, preferred, dispatch);
twin_time_t delay = animation->gif->frame_delays[animation->gif->current_frame];
animation->timeout =
twin_set_timeout(_apps_animation_timeout, delay, animation);
}

static apps_animation_t *apps_animation_create(twin_box_t *parent,
twin_animation_t *gif)
{
apps_animation_t *animation = malloc(sizeof(apps_animation_t));
animation->gif = gif;
_apps_animation_init(animation, parent, _apps_animation_dispatch);
return animation;
}

void apps_animation_start(twin_screen_t *screen, const char *path, int x, int y)
{
twin_animation_t *gif = twin_animation_from_file(path);
twin_toplevel_t *toplevel =
twin_toplevel_create(screen, TWIN_ARGB32, TwinWindowApplication, x, y,
gif->width, gif->height, path);
apps_animation_t *animation = apps_animation_create(&toplevel->box, gif);
(void) animation;
twin_toplevel_show(toplevel);
}
17 changes: 17 additions & 0 deletions apps/apps_animation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2004 Keith Packard <[email protected]>
* 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,
int x,
int y);

#endif /* _APPS_ANIMATION_H_ */
4 changes: 4 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,9 @@ 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, 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.
4 changes: 4 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ config DEMO_SPLINE
default y
depends on DEMO_APPLICATIONS

config DEMO_ANIMATION
bool "Build animation demo"
default y
depends on DEMO_APPLICATIONS
endmenu
1 change: 1 addition & 0 deletions configs/defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ CONFIG_DEMO_CLOCK=y
CONFIG_DEMO_CALCULATOR=y
CONFIG_DEMO_LINE=y
CONFIG_DEMO_SPLINE=y
CONFIG_DEMO_ANIMATION=y
27 changes: 27 additions & 0 deletions include/twin.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,21 @@ typedef struct _twin_pixmap {
twin_window_t *window;
} twin_pixmap_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_count_t *frame_delays;
/* Whether the animation should loop */
bool loop;
/* Current frame index */
twin_count_t current_frame;
twin_coord_t width; /* pixels */
twin_coord_t height; /* pixels */
} twin_animation_t;

/*
* twin_put_begin_t: called before data are drawn to the screen
* twin_put_span_t: called for each scanline drawn
Expand Down Expand Up @@ -692,6 +707,18 @@ void twin_icon_draw(twin_pixmap_t *pixmap,

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

/*
* gif.c
*/

twin_animation_t *twin_animation_from_file(const char *path);

twin_pixmap_t *twin_animation_get_current_frame(twin_animation_t *animation);

void twin_animation_advance_frame(twin_animation_t *animation);

void twin_animation_destroy(twin_animation_t *animation);

/*
* label.c
*/
Expand Down
111 changes: 111 additions & 0 deletions src/gif.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gifdec.h"
#include "twin.h"

twin_animation_t *twin_animation_from_file(const char *path)
{
twin_animation_t *animation = malloc(sizeof(twin_animation_t));
if (!animation)
return NULL;

gd_GIF *gif = gd_open_gif(path);
if (!gif) {
free(animation);
return NULL;
}

animation->n_frames = 0;
animation->frames = NULL;
animation->frame_delays = NULL;
animation->loop = gif->loop_count == 0;
animation->current_frame = 0;
animation->width = gif->width;
animation->height = gif->height;

int frame_count = 0;
while (gd_get_frame(gif)) {
frame_count++;
}

animation->n_frames = frame_count;
animation->frames = malloc(sizeof(twin_pixmap_t *) * frame_count);
animation->frame_delays = malloc(sizeof(twin_count_t) * frame_count);

gd_rewind(gif);
uint8_t *color, *frame;
frame = malloc(gif->width * gif->height * 3);
if (!frame) {
free(animation);
gd_close_gif(gif);
return NULL;
}
for (twin_count_t i = 0; i < frame_count; i++) {
animation->frames[i] =
twin_pixmap_create(TWIN_ARGB32, gif->width, gif->height);
animation->frames[i]->format = TWIN_ARGB32;

gd_render_frame(gif, frame);
color = frame;
twin_pointer_t p = twin_pixmap_pointer(animation->frames[i], 0, 0);
twin_coord_t row = 0, col = 0;
for (int j = 0; j < gif->width * gif->height; j++) {
uint8_t r = *(color++);
uint8_t g = *(color++);
uint8_t b = *(color++);
if (!gd_is_bgcolor(gif, color))
*(p.argb32++) = 0xFF000000U | (r << 16) | (g << 8) | b;
/* Construct background */
else if (((row >> 3) + (col >> 3)) & 1)
*(p.argb32++) = 0xFFAFAFAFU;
else
*(p.argb32++) = 0xFF7F7F7FU;
col++;
if (col == gif->width) {
row++;
col = 0;
}
}

animation->frame_delays[i] =
gif->gce.delay * 10; // GIF delay in units of 1/100 second
gd_get_frame(gif);
}
gd_close_gif(gif);
return animation;
}

twin_pixmap_t *twin_animation_get_current_frame(twin_animation_t *animation)
{
if (!animation || animation->current_frame >= animation->n_frames) {
return NULL;
}
return animation->frames[animation->current_frame];
}

void twin_animation_advance_frame(twin_animation_t *animation)
{
if (!animation)
return;

animation->current_frame++;
if (animation->current_frame >= animation->n_frames) {
if (animation->loop) {
animation->current_frame = 0;
}
}
}

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

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

0 comments on commit 37af53a

Please sign in to comment.