From 8c3a07294b612cf7d1be350c802282b7357b9948 Mon Sep 17 00:00:00 2001 From: Bill Kendrick Date: Sat, 25 Jul 2020 00:18:46 -0700 Subject: [PATCH] Beginning GIF export Beginning addition of an option to export animated GIFs from the Open -> Slideshow dialog, after choosing the images. Non-operable at this time, but a button has been added (and will provide a hint to select 2 or more images, when clicked). --- Makefile | 18 ++- data/images/ui/gif_export.png | Bin 0 -> 1521 bytes docs/AUTHORS.txt | 4 + docs/CHANGES.txt | 11 +- src/gifenc.c | 287 ++++++++++++++++++++++++++++++++++ src/gifenc.h | 24 +++ src/tuxpaint.c | 80 ++++++++-- 7 files changed, 408 insertions(+), 16 deletions(-) create mode 100644 data/images/ui/gif_export.png create mode 100644 src/gifenc.c create mode 100644 src/gifenc.h diff --git a/Makefile b/Makefile index 4071de5f4..13841f57c 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # Various contributors (see AUTHORS.txt) # http://www.tuxpaint.org/ -# June 14, 2002 - June 22, 2020 +# June 14, 2002 - July 24, 2020 # The version number, for release: @@ -115,10 +115,10 @@ PNG:=$(if $(PNG),$(PNG),$(call linktest,-lpng12,)) FRIBIDI_LIB:=$(shell $(PKG_CONFIG) --libs fribidi) FRIBIDI_CFLAGS:=$(shell $(PKG_CONFIG) --cflags fribidi) -windows_ARCH_LINKS:=-lintl $(PNG) -lzdll -lwinspool -lshlwapi $(FRIBIDI_LIB) -liconv -osx_ARCH_LINKS:=$(FRIBIDI_LIB) -beos_ARCH_LINKS:=-lintl $(PNG) -lz -lbe -lnetwork -liconv $(FRIBIDI_LIB) $(PAPER_LIB) $(STDC_LIB) -linux_ARCH_LINKS:=$(PAPER_LIB) $(FRIBIDI_LIB) +windows_ARCH_LINKS:=-lintl $(PNG) -lzdll -lwinspool -lshlwapi $(FRIBIDI_LIB) -liconv -limagequant +osx_ARCH_LINKS:=$(FRIBIDI_LIB) -limagequant +beos_ARCH_LINKS:=-lintl $(PNG) -lz -lbe -lnetwork -liconv $(FRIBIDI_LIB) $(PAPER_LIB) $(STDC_LIB) -limagequant +linux_ARCH_LINKS:=$(PAPER_LIB) $(FRIBIDI_LIB) -limagequant ARCH_LINKS:=$($(OS)_ARCH_LINKS) windows_ARCH_HEADERS:=src/win32_print.h @@ -1035,6 +1035,7 @@ TuxPaint.dmg: tuxpaint: obj/tuxpaint.o obj/i18n.o obj/im.o obj/cursor.o obj/pixels.o \ obj/rgblinear.o obj/playsound.o obj/fonts.o obj/parse.o obj/fill.o \ obj/progressbar.o obj/dirwalk.o obj/get_fname.o obj/onscreen_keyboard.o \ + obj/gifenc.o \ $(ARCH_LIBS) @echo @echo "...Linking Tux Paint..." @@ -1055,6 +1056,7 @@ obj/tuxpaint.o: src/tuxpaint.c \ src/tools.h src/titles.h src/colors.h src/shapes.h \ src/sounds.h src/tip_tux.h src/great.h \ src/tp_magic_api.h src/parse.h src/onscreen_keyboard.h \ + src/gifenc.h \ src/$(MOUSEDIR)/arrow.xbm src/$(MOUSEDIR)/arrow-mask.xbm \ src/$(MOUSEDIR)/hand.xbm src/$(MOUSEDIR)/hand-mask.xbm \ src/$(MOUSEDIR)/insertion.xbm \ @@ -1140,6 +1142,12 @@ obj/pixels.o: src/pixels.c src/pixels.h src/compiler.h src/debug.h @$(CC) $(CFLAGS) $(DEBUG_FLAGS) $(SDL_CFLAGS) $(DEFS) \ -c src/pixels.c -o obj/pixels.o +obj/gifenc.o: src/gifenc.c src/gifenc.h + @echo + @echo "...Compiling animated GIF export libary..." + @$(CC) $(CFLAGS) $(DEBUG_FLAGS) $(DEFS) \ + -c src/gifenc.c -o obj/gifenc.o + obj/playsound.o: src/playsound.c src/playsound.h \ src/compiler.h src/debug.h @echo diff --git a/data/images/ui/gif_export.png b/data/images/ui/gif_export.png new file mode 100644 index 0000000000000000000000000000000000000000..3a53800e739a445821cb1a204936f12e561bbd11 GIT binary patch literal 1521 zcmV zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KJuG}CDh2L34mw;`I&2k7oQg_hh_dqgpXWHBL zUbQckCMY3e9OInN5KS20zRvIk2U*M^sd+9rM;xi7!WB0j&!g-s#k8;c;M*8I+XIbj zfMF8!a{jb>`B&KW?SXroE7&=N-{H}a3@UZSM8Z6TB<`!9K60KFr{T(ymY)djNi?7nxn%-Do?h?P z$M`7FlgPKb@T2-IzArx4%R_$9GG7|u=G_6(_r>25KTIt5jQDxL*(gF8i^JhKfa4N55J@^H^g8@(v9Sv_^#(RA!(=ohnTlyKmHx zV&cJup1HAXn~QPb24W;>HJWTvq>F=G;}I4!;d?H$=Pi4_Mvj>~!IcTY2#@@-h2IT+ zriD2daH1tlEOP4#F94x=anmjEtzG!zLt&v*K~T5Mju)))I71vtZ?z?x z^P;Qh&m}~``V{~YVQxh-Bp?to>Yg%1V>Tk_=m1oa`C1zX2#_jwB*{r6b1-&ZbK@DK z?aRuUml=B#K&WJ_EeD|rSScy$$C9Ils){C6&1&jFi`FbTWz9KTUYlGsv1Dr5%-o7q z7f-I9-Q2x+EnEb9pq5;$cqye;4iyts#H#SGupE5Ikq$lbVTT{(s14=QQqz{3HE*TW z&RsgTiJrT4@1@s4VPK~iY3RtqhL1ApM6FGkY3j_=rq43#o!Uk9r}hbIbW!6?YWdln z8mz(W7J}AxqKg@baUu}6MF0iOi&=C^i5I!WES9yWDI-Z;Y(l3+3<%R8)=77EZ{$A2 zEvSEp8~=%1Sm^!*xd3!Ob9;wcuX`Vs&26p1rD+tbK8z2qfxWJeTK(Jp73dY{73dY{ z73hBtH1XBIznAbG=X4-vO@?C90003aX+uL$Nkc;*aB^>EX>4Tx0C=38kg-a`P!xv0 zRB5H?qzH;b1lNL#nOv$u5ENppV0B896pKj=NlNhzd=q_`4uaql_yh{h{x>=}n8gc+ z|Ko7Z|KEF|WhY@;F785_RfX&KhnK@EyYohe4cc_s4#G0;d)^6+=kpcI=u6LdHP8Kd z+EHAF(zz-N^P&Q4U+m1P+^~wZ+kt;5eh}MHnnmI#@lKRRhRv>@Ce!f8)7)k`&MwYP zU$uqHF{gOgjF>VZp`s_v)H%XoPwG+-a8F4nO^DT-X{O>vRLaTaKr|5PHVt{3b)Kh` zkvt~yG6{F2@-%sv|NUP3IIB0U^?4o?LETzVwZ>z3e}t7GtUg2cx*==n_uCp~oOI93iTgv8pnLdbR&Xq8(Aq*mx>@Ir zH>wmd84-AtDu(bZ$62!zv^eO-IHt}vIaG$2BgG=S5*de>wy5Z~HeHm+(kak(u6UtW zqR?<(8@s|KbS8Nc@hh_oQ2SdCe+V^_TnPDXXh4}VACg-UV#2TSE*Bf+nGHdHBU)~F z4CTPRWKGPx!8|b_!ilu5eYS-E=vH!rdj|P_EOG$!p-2f!WWD#W&MD*cJF-by{Vet- z6?W(l@i40{3V$_acwmMjZZWwACRkvPDL;g8&M3>ISfRot)11*0KCqsqgdhF?`{+Lb XWXQv^zQDnx00000NkvXXu0mjf3?jpu literal 0 HcmV?d00001 diff --git a/docs/AUTHORS.txt b/docs/AUTHORS.txt index 2ce7fb942..af081a986 100644 --- a/docs/AUTHORS.txt +++ b/docs/AUTHORS.txt @@ -103,6 +103,10 @@ $Id$ which was from "Gimp-gluas" plug-in for The GIMP: http://pippin.gimp.org/plug-ins/gluas/ + Animated GIF export from "gifenc", + https://github.com/lecram/gifenc, + by Marcel Rodrigues + Mouse accessibility code and keyboard access Ankit Choudary , as part of GSOC 2010, with integration and fixes by Pere Pujal i Carabantes diff --git a/docs/CHANGES.txt b/docs/CHANGES.txt index 5324453cb..4c0cffa37 100644 --- a/docs/CHANGES.txt +++ b/docs/CHANGES.txt @@ -8,7 +8,16 @@ http://www.tuxpaint.org/ $Id$ -2020.June.22 (0.9.25) +2020.July.24 (0.9.25) + * New Features + ------------ + * [WIP] GIF export option from Open -> Slideshow dialog. + * Utilizes "gifenc", public domain by Marcel Rodrigues: + https://github.com/lecram/gifenc + * Depends on "libimagequant", from the "pngquant2" project: + https://github.com/ImageOptim/libimagequant + (GPL v3 or later, for Free/Libre Open Source Software) + * Ports & Building ---------------- * Corrections for Haiku not opening saved files. diff --git a/src/gifenc.c b/src/gifenc.c new file mode 100644 index 000000000..640315266 --- /dev/null +++ b/src/gifenc.c @@ -0,0 +1,287 @@ +#include "gifenc.h" + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +/* helper to write a little-endian 16-bit number portably */ +#define write_num(fd, n) write((fd), (uint8_t []) {(n) & 0xFF, (n) >> 8}, 2) + +static uint8_t vga[0x30] = { + 0x00, 0x00, 0x00, + 0xAA, 0x00, 0x00, + 0x00, 0xAA, 0x00, + 0xAA, 0x55, 0x00, + 0x00, 0x00, 0xAA, + 0xAA, 0x00, 0xAA, + 0x00, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, + 0x55, 0x55, 0x55, + 0xFF, 0x55, 0x55, + 0x55, 0xFF, 0x55, + 0xFF, 0xFF, 0x55, + 0x55, 0x55, 0xFF, + 0xFF, 0x55, 0xFF, + 0x55, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, +}; + +struct Node { + uint16_t key; + struct Node *children[]; +}; +typedef struct Node Node; + +static Node * +new_node(uint16_t key, int degree) +{ + Node *node = calloc(1, sizeof(*node) + degree * sizeof(Node *)); + if (node) + node->key = key; + return node; +} + +static Node * +new_trie(int degree, int *nkeys) +{ + Node *root = new_node(0, degree); + /* Create nodes for single pixels. */ + for (*nkeys = 0; *nkeys < degree; (*nkeys)++) + root->children[*nkeys] = new_node(*nkeys, degree); + *nkeys += 2; /* skip clear code and stop code */ + return root; +} + +static void +del_trie(Node *root, int degree) +{ + if (!root) + return; + for (int i = 0; i < degree; i++) + del_trie(root->children[i], degree); + free(root); +} + +static void put_loop(ge_GIF *gif, uint16_t loop); + +ge_GIF * +ge_new_gif( + const char *fname, uint16_t width, uint16_t height, + uint8_t *palette, int depth, int loop +) +{ + int i, r, g, b, v; + ge_GIF *gif = calloc(1, sizeof(*gif) + 2*width*height); + if (!gif) + goto no_gif; + gif->w = width; gif->h = height; + gif->depth = depth > 1 ? depth : 2; + gif->frame = (uint8_t *) &gif[1]; + gif->back = &gif->frame[width*height]; + gif->fd = creat(fname, 0666); + if (gif->fd == -1) + goto no_fd; +#ifdef _WIN32 + setmode(gif->fd, O_BINARY); +#endif + write(gif->fd, "GIF89a", 6); + write_num(gif->fd, width); + write_num(gif->fd, height); + write(gif->fd, (uint8_t []) {0xF0 | (depth-1), 0x00, 0x00}, 3); + if (palette) { + write(gif->fd, palette, 3 << depth); + } else if (depth <= 4) { + write(gif->fd, vga, 3 << depth); + } else { + write(gif->fd, vga, sizeof(vga)); + i = 0x10; + for (r = 0; r < 6; r++) { + for (g = 0; g < 6; g++) { + for (b = 0; b < 6; b++) { + write(gif->fd, (uint8_t []) {r*51, g*51, b*51}, 3); + if (++i == 1 << depth) + goto done_gct; + } + } + } + for (i = 1; i <= 24; i++) { + v = i * 0xFF / 25; + write(gif->fd, (uint8_t []) {v, v, v}, 3); + } + } +done_gct: + if (loop >= 0 && loop <= 0xFFFF) + put_loop(gif, (uint16_t) loop); + return gif; +no_fd: + free(gif); +no_gif: + return NULL; +} + +static void +put_loop(ge_GIF *gif, uint16_t loop) +{ + write(gif->fd, (uint8_t []) {'!', 0xFF, 0x0B}, 3); + write(gif->fd, "NETSCAPE2.0", 11); + write(gif->fd, (uint8_t []) {0x03, 0x01}, 2); + write_num(gif->fd, loop); + write(gif->fd, "\0", 1); +} + +/* Add packed key to buffer, updating offset and partial. + * gif->offset holds position to put next *bit* + * gif->partial holds bits to include in next byte */ +static void +put_key(ge_GIF *gif, uint16_t key, int key_size) +{ + int byte_offset, bit_offset, bits_to_write; + byte_offset = gif->offset / 8; + bit_offset = gif->offset % 8; + gif->partial |= ((uint32_t) key) << bit_offset; + bits_to_write = bit_offset + key_size; + while (bits_to_write >= 8) { + gif->buffer[byte_offset++] = gif->partial & 0xFF; + if (byte_offset == 0xFF) { + write(gif->fd, "\xFF", 1); + write(gif->fd, gif->buffer, 0xFF); + byte_offset = 0; + } + gif->partial >>= 8; + bits_to_write -= 8; + } + gif->offset = (gif->offset + key_size) % (0xFF * 8); +} + +static void +end_key(ge_GIF *gif) +{ + int byte_offset; + byte_offset = gif->offset / 8; + if (gif->offset % 8) + gif->buffer[byte_offset++] = gif->partial & 0xFF; + write(gif->fd, (uint8_t []) {byte_offset}, 1); + write(gif->fd, gif->buffer, byte_offset); + write(gif->fd, "\0", 1); + gif->offset = gif->partial = 0; +} + +static void +put_image(ge_GIF *gif, uint16_t w, uint16_t h, uint16_t x, uint16_t y) +{ + int nkeys, key_size, i, j; + Node *node, *child, *root; + int degree = 1 << gif->depth; + + write(gif->fd, ",", 1); + write_num(gif->fd, x); + write_num(gif->fd, y); + write_num(gif->fd, w); + write_num(gif->fd, h); + write(gif->fd, (uint8_t []) {0x00, gif->depth}, 2); + root = node = new_trie(degree, &nkeys); + key_size = gif->depth + 1; + put_key(gif, degree, key_size); /* clear code */ + for (i = y; i < y+h; i++) { + for (j = x; j < x+w; j++) { + uint8_t pixel = gif->frame[i*gif->w+j] & (degree - 1); + child = node->children[pixel]; + if (child) { + node = child; + } else { + put_key(gif, node->key, key_size); + if (nkeys < 0x1000) { + if (nkeys == (1 << key_size)) + key_size++; + node->children[pixel] = new_node(nkeys++, degree); + } else { + put_key(gif, degree, key_size); /* clear code */ + del_trie(root, degree); + root = node = new_trie(degree, &nkeys); + key_size = gif->depth + 1; + } + node = root->children[pixel]; + } + } + } + put_key(gif, node->key, key_size); + put_key(gif, degree + 1, key_size); /* stop code */ + end_key(gif); + del_trie(root, degree); +} + +static int +get_bbox(ge_GIF *gif, uint16_t *w, uint16_t *h, uint16_t *x, uint16_t *y) +{ + int i, j, k; + int left, right, top, bottom; + left = gif->w; right = 0; + top = gif->h; bottom = 0; + k = 0; + for (i = 0; i < gif->h; i++) { + for (j = 0; j < gif->w; j++, k++) { + if (gif->frame[k] != gif->back[k]) { + if (j < left) left = j; + if (j > right) right = j; + if (i < top) top = i; + if (i > bottom) bottom = i; + } + } + } + if (left != gif->w && top != gif->h) { + *x = left; *y = top; + *w = right - left + 1; + *h = bottom - top + 1; + return 1; + } else { + return 0; + } +} + +static void +set_delay(ge_GIF *gif, uint16_t d) +{ + write(gif->fd, (uint8_t []) {'!', 0xF9, 0x04, 0x04}, 4); + write_num(gif->fd, d); + write(gif->fd, "\0\0", 2); +} + +void +ge_add_frame(ge_GIF *gif, uint16_t delay) +{ + uint16_t w, h, x, y; + uint8_t *tmp; + + if (delay) + set_delay(gif, delay); + if (gif->nframes == 0) { + w = gif->w; + h = gif->h; + x = y = 0; + } else if (!get_bbox(gif, &w, &h, &x, &y)) { + /* image's not changed; save one pixel just to add delay */ + w = h = 1; + x = y = 0; + } + put_image(gif, w, h, x, y); + gif->nframes++; + tmp = gif->back; + gif->back = gif->frame; + gif->frame = tmp; +} + +void +ge_close_gif(ge_GIF* gif) +{ + write(gif->fd, ";", 1); + close(gif->fd); + free(gif); +} diff --git a/src/gifenc.h b/src/gifenc.h new file mode 100644 index 000000000..b8504e0c2 --- /dev/null +++ b/src/gifenc.h @@ -0,0 +1,24 @@ +#ifndef GIFENC_H +#define GIFENC_H + +#include + +typedef struct ge_GIF { + uint16_t w, h; + int depth; + int fd; + int offset; + int nframes; + uint8_t *frame, *back; + uint32_t partial; + uint8_t buffer[0xFF]; +} ge_GIF; + +ge_GIF *ge_new_gif( + const char *fname, uint16_t width, uint16_t height, + uint8_t *palette, int depth, int loop +); +void ge_add_frame(ge_GIF *gif, uint16_t delay); +void ge_close_gif(ge_GIF* gif); + +#endif /* GIFENC_H */ diff --git a/src/tuxpaint.c b/src/tuxpaint.c index 0632bcce8..514033004 100644 --- a/src/tuxpaint.c +++ b/src/tuxpaint.c @@ -483,6 +483,8 @@ static void mtw(wchar_t * wtok, char *tok) #error "---------------------------------------------------" #endif +#include "libimagequant.h" + #include "SDL_getenv.h" #include "i18n.h" @@ -495,6 +497,7 @@ static void mtw(wchar_t * wtok, char *tok) #include "dirwalk.h" #include "get_fname.h" #include "onscreen_keyboard.h" +#include "gifenc.h" #include "tools.h" #include "titles.h" @@ -1432,7 +1435,7 @@ static SDL_Surface *img_black, *img_grey; static SDL_Surface *img_yes, *img_no; static SDL_Surface *img_sfx, *img_speak; static SDL_Surface *img_open, *img_erase, *img_back, *img_trash; -static SDL_Surface *img_slideshow, *img_play, *img_select_digits; +static SDL_Surface *img_slideshow, *img_play, *img_gif_export, *img_select_digits; static SDL_Surface *img_printer, *img_printer_wait; static SDL_Surface *img_save_over, *img_popup_arrow; static SDL_Surface *img_cursor_up, *img_cursor_down; @@ -1755,7 +1758,8 @@ static short *brushes_directional = NULL; static SDL_Surface *img_shapes[NUM_SHAPES], *img_shape_names[NUM_SHAPES]; static SDL_Surface *img_openlabels_open, *img_openlabels_erase, - *img_openlabels_slideshow, *img_openlabels_back, *img_openlabels_play, *img_openlabels_next; + *img_openlabels_slideshow, *img_openlabels_back, *img_openlabels_play, + *img_openlabels_gif_export, *img_openlabels_next; static SDL_Surface *img_tux[NUM_TIP_TUX]; @@ -7861,11 +7865,14 @@ static void create_button_labels(void) /* Open dialog: 'Back' button, to dismiss Open dialog without opening a picture */ img_openlabels_back = do_render_button_label(gettext_noop("Back")); - /* Slideshow: 'Next' button, to load next slide (image) */ - img_openlabels_next = do_render_button_label(gettext_noop("Next")); - /* Slideshow: 'Play' button, to begin a slideshow sequence */ img_openlabels_play = do_render_button_label(gettext_noop("Play")); + + /* Slideshow: 'GIF Export' button, to create an animated GIF */ + img_openlabels_gif_export = do_render_button_label(gettext_noop("GIF Export")); + + /* Slideshow: 'Next' button, to load next slide (image) */ + img_openlabels_next = do_render_button_label(gettext_noop("Next")); } @@ -15029,7 +15036,7 @@ static int do_slideshow(void) SDL_Rect dest; SDL_Event event; SDLKey key; - char *freeme; + char *freeme, *instructions; int speeds; float x_per, y_per; int xx, yy; @@ -15278,9 +15285,8 @@ static int do_slideshow(void) /* Let user choose images: */ /* Instructions for Slideshow file dialog (FIXME: Make a #define) */ - freeme = textdir(gettext_noop("Choose the pictures you want, " "then click “Play”.")); - draw_tux_text(TUX_BORED, freeme, 1); - free(freeme); + instructions = textdir(gettext_noop("Choose the pictures you want, " "then click “Play”.")); + draw_tux_text(TUX_BORED, instructions, 1); /* NOTE: cur is now set above; if file_id'th file is found, it's set to that file's index; otherwise, we default to '0' */ @@ -15386,6 +15392,21 @@ static int do_slideshow(void) SDL_BlitSurface(img_openlabels_play, NULL, screen, &dest); + /* "GIF Export" button: */ + + dest.x = WINDOW_WIDTH - 96 - 48 * 2; + dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48; + SDL_BlitSurface(img_btn_up, NULL, screen, &dest); + + dest.x = WINDOW_WIDTH - 96 - 48 * 2 + (48 - img_gif_export->w) / 2; + dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - 48; + SDL_BlitSurface(img_gif_export, NULL, screen, &dest); + + dest.x = WINDOW_WIDTH - 96 - 48 * 2 + (48 - img_openlabels_gif_export->w) / 2; + dest.y = (48 * 7 + 40 + HEIGHTOFFSET) - img_openlabels_gif_export->h; + SDL_BlitSurface(img_openlabels_gif_export, NULL, screen, &dest); + + /* "Back" button: */ dest.x = WINDOW_WIDTH - 96 - 48; @@ -15633,6 +15654,31 @@ static int do_slideshow(void) update_list = 1; } } + else if (event.button.x >= (WINDOW_WIDTH - 96 - 48 - 48) && + event.button.x < (WINDOW_WIDTH - 96 - 48) && + event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 && + event.button.y < (48 * 7 + 40 + HEIGHTOFFSET)) + { + /* GIF Export */ + + playsound(screen, 1, SND_CLICK, 1, SNDPOS_RIGHT, SNDDIST_NEAR); + + if (num_selected < 2) + { + /* None selected? Too dangerous to select all. + Only 1 selected? No point in saving as GIF. + */ + freeme = textdir(gettext_noop("Select 2 or more drawings to turn into an animated GIF.")); + draw_tux_text(TUX_BORED, freeme, 1); + free(freeme); + + control_drawtext_timer(2000, instructions, 0); /* N.B. It will draw instructions, regardless */ + } + else + { + /* FIXME: Do it */ + } + } else if (event.button.x >= (WINDOW_WIDTH - 96 - 48) && event.button.x < (WINDOW_WIDTH - 96) && event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 && @@ -15696,7 +15742,7 @@ static int do_slideshow(void) do_setcursor(cursor_down); } else if (((event.button.x >= 96 && event.button.x < 96 + 48 + 96) || - (event.button.x >= (WINDOW_WIDTH - 96 - 48) && + (event.button.x >= (WINDOW_WIDTH - 96 - 48 * 2) && event.button.x < (WINDOW_WIDTH - 96))) && event.button.y >= (48 * 7 + 40 + HEIGHTOFFSET) - 48 && event.button.y < (48 * 7 + 40 + HEIGHTOFFSET)) @@ -15736,6 +15782,17 @@ static int do_slideshow(void) else if (event.type == SDL_JOYBUTTONDOWN || event.type == SDL_JOYBUTTONUP) handle_joybuttonupdown(event, oldpos_x, oldpos_y); + + else if (event.type == SDL_USEREVENT) + { + if (event.user.code == USEREVENT_TEXT_UPDATE) + { + if (event.user.data1 != NULL) + { + draw_tux_text(TUX_BORED, instructions, 1); + } + } + } } if (motioner | hatmotioner) @@ -15764,6 +15821,8 @@ static int do_slideshow(void) free(d_exts); free(selected); + control_drawtext_timer(0, "", 0); + free(instructions); return go_back; } @@ -23990,6 +24049,7 @@ static void setup(void) img_slideshow = loadimage(DATA_PREFIX "images/ui/slideshow.png"); img_play = loadimage(DATA_PREFIX "images/ui/play.png"); + img_gif_export = loadimage(DATA_PREFIX "images/ui/gif_export.png"); img_select_digits = loadimage(DATA_PREFIX "images/ui/select_digits.png"); img_popup_arrow = loadimage(DATA_PREFIX "images/ui/popup_arrow.png");