Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(1 edit) (+7)

The v1.14 release introduced a generalization for read[], allowing scripts to break an animated GIF image down into frames. There are many interesting ways to take advantage of this functionality. For example,

The "gif" Contraption:

At last, an easy way to import animated GIFs into decks! This contraption prompts the user to select a GIF file, unpacks and dithers it to 1-bit, automatically resizes itself to match the size of the image, and then loops the frames at 30fps:

Note that using GIF widgets can quickly expand the size of your decks- use them sparingly, and avoid importing huge or overly-long animations! Decker's GIF loader is brand-new and may have some quirks to hammer out, so remember to save often while playing with this feature.

%%WGT0{"w":[{"name":"gif1","type":"contraption","size":[100,100],"pos":[206,121],"def":"gif","widgets":{"c":{},"f":{},"b":{}}}],"d":{"gif":{"name":"gif","size":[100,100],"resizable":1,"margin":[0,0,0,0],"description":"Import and play animated gifs. Careful: huge gifs can quickly bloat the size of your deck!","script":"on view do\n fr:extract arg where arg..type=\"image\" from f.value\n c.show:card.show\n c.clear[]\n if count fr\n  b.show:\"none\"\n  i:fr[(count fr)%sys.ms/2*60]\n  card.size:i.size\n  c.paste[i]\n else\n  b.show:\"solid\"\n end\nend","widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"animated":1,"border":0,"scale":1},"f":{"type":"field","size":[73,35],"pos":[8,-50],"locked":1},"b":{"type":"button","size":[69,20],"pos":[16,40],"script":"on click do\n g:\"[255,0,255,246,148,108,132,51,61,147,144,86,66,111,185,134,69,0]\"\n grays:(0,1,32+range 16) dict \"%j\" parse g\n f.value:raze each i in read[\"image\" \"frames\"].frames\n  rtext.make[\"\" \"\" i.map[grays].transform[\"dither\"]]\n end\n view[]\nend","text":"Open Gif..."}}}}}

The "scrubber" Contraption:

This contraption doesn't animate automatically; instead, it allows a user to manually scrub back and forth through the frames of the animation. It also demonstrates importing color images (resampled to the Decker 16-color palette) instead of 1-bit dithering.

%%WGT0{"w":[{"name":"scrubber1","type":"contraption","size":[100,100],"pos":[49,108],"def":"scrubber","widgets":{"c":{},"f":{},"b":{},"s":{}}}],"d":{"scrubber":{"name":"scrubber","size":[100,100],"resizable":1,"margin":[0,0,0,19],"description":"Import animated gifs and allow the user to manually scrub through the frames. Careful: huge gifs can quickly bloat the size of your deck!","script":"on change do\n view[]\nend\n\non view do\n fr:extract arg where arg..type=\"image\" from f.value\n s.interval:0,(count fr)-1\n c.show:card.show\n c.clear[]\n if count fr\n  b.show:\"none\"\n  i:fr[s.value]\n  card.size:i.size+s.size*0,1\n  c.paste[i]\n else\n  b.show:\"solid\"\n end\nend","widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"border":1,"scale":1},"f":{"type":"field","size":[73,35],"pos":[8,-50],"locked":1},"b":{"type":"button","size":[69,20],"pos":[16,40],"script":"on click do\n f.value:raze each i in read[\"image\" \"frames\"].frames\n  rtext.make[\"\" \"\" i]\n end\n view[]\nend","text":"Open Gif..."},"s":{"type":"slider","size":[100,17],"pos":[0,83],"interval":[-1,0]}}}}}

There are many variations possible on the above ideas that you might want, like looping back-and-forth, rescaling imported images to fit the bounding box of the contraption, or firing events to drive other animations. Feel free to share your own takes!

(+1)

Hello, I've been poking around in the wonders of decker recently and reading up as much as I can, but I couldn't figure out how to use this contraption for colored gifs. Is there a way, and if so, do you think you could help? Thanks!

(1 edit) (+1)

The "gif" contraption is designed to read gifs as a series of grayscale frames and dither them.

We could make it handle color by replacing that dithering code, which is in the script of the "b" button,

 g:"[255,0,255,246,148,108,132,51,61,147,144,86,66,111,185,134,69,0]"
 grays:(0,1,32+range 16) dict "%j" parse g
 f.value:raze each i in read["image" "frames"].frames
  rtext.make["" "" i.map[grays].transform["dither"]]
 end

With something based on the equivalent in the "scrubber" contraption:

 f.value:raze each i in read["image" "frames"].frames
  rtext.make["" "" i]
 end

All together as a copyable snippet:

%%WGT0{"w":[{"name":"gif1","type":"contraption","size":[100,100],"pos":[206,121],"def":"colorgif","widgets":{"c":{},"f":{},"b":{}}}],"d":{"colorgif":{"name":"colorgif","size":[100,100],"resizable":1,"margin":[0,0,0,0],"description":"Import and play animated 16-color gifs. Careful: huge gifs can quickly bloat the size of your deck!","script":"on view do\n fr:extract arg where arg..type=\"image\" from f.value\n c.show:card.show\n c.clear[]\n if count fr\n  b.show:\"none\"\n  i:fr[(count fr)%sys.ms/2*60]\n  card.size:i.size\n  c.paste[i]\n else\n  b.show:\"solid\"\n end\nend","widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"animated":1,"border":0,"scale":1},"f":{"type":"field","size":[73,35],"pos":[8,-50],"locked":1},"b":{"type":"button","size":[69,20],"pos":[16,40],"script":"on click do\n f.value:raze each i in read[\"image\" \"frames\"].frames\n  rtext.make[\"\" \"\" i]\n end\n view[]\nend","text":"Open Gif..."}}}}}

Note that this is still ultimately limited by Decker's 16-color palette; colors not in the palette are converted into the current palette via a minimum-squared-distance heuristic, which doesn't always do a fantastic job.


For best results you might need to customize Decker's palette and/or use external tools like imagemagick to adjust the GIF's palette. If you just want a little motion and color in your decks, you could instead directly import the "wiggler" contraption from wigglypaint.

Does any of that help point you in the right direction?

(+1)

Oh this should be perfect, yes, thank you! I had already read up on the limited palette and spent a few good hours figuring imagemagick out to adjust gifs, so that should be easy enough now. Thank you kindly!