Requires BBPack:
pastebin get cUYTGbpb bbpack
So yeah, a GIF decoder (and encoder). You hand it a GIF and it draws it. If it's an animation, it can handle that too. Fairly straight-forward.
Video
http://www.youtube.com/watch?v=Db-UdqSQRL8Although it currently tends to stall, skyTerm (also demonstrated in the video) can be found here.
CC 1.74+ gets a better rendering rate, though updated versions of the API run at about full speed on much older ComputerCraft builds.
For reference, a regular computer display has 51x19 characters available for rendering. A full-sized monitor, with a text scale of 0.5, has 164x81 characters. If you want some suitable images, try a web search, or perhaps this site. The API can resize loaded images to fit your display, but the process is processor-intensive compared to using an external tool.
Usage
GIF.loadGIF(string targetFile [, string palette file]) => table imageDataReturns a table filled with numerically indexed subtables, one for each image in the GIF:
Loaded GIF structure
{
["backgroundCol"] = (number) colour. When calling GIF.animateGIF(), used for transparent pixels. Calling GIF.drawGIF() leaves the background undisturbed
["backgroundCol2"] = (string) colour. Paintutils representation; eg colours.red would be "e"
["width"] = (number) image width
["height"] = (number) image height
["pal"] = (table) sub-tables containg RGB values for each colour in the image, stored per the pattern {{R,G,B},{R,G,B},...}. Only present if a palette was specified during image loading.
-- First frame in GIF (non-animations only have a single frame):
[1] = {
["xstart"] = (number) columns. Start rendering this frame at the GIF's position, plus this many columns
["ystart"] = (number) rows. Start rendering this frame at the GIF's position, plus this many rows
["xend"] = (number) frame width
["yend"] = (number) frame height
["delay"] = (number) seconds. Sleep this duration after rendering the frame, before drawing the next
["disposal"] = (number) undraw method. 1 = Leave alone, 2 = After the delay, clear the frame's area with the image's background colour immediately before drawing the next frame
First row of the frame's pixel data:
[1] = {(number) skip / (string) blit, (number) skip / (string) blit, (number) skip / (string) blit, etc...}
When rendering the data in a given row's table, if a number is encountered, add that to the column position of the cursor. If a string is encountered, term.blit() with it as the text background. Continue until the end of the row's table
[2] = Second row
...
If a palette was specified during image loading, the strings are replaced with sub-tables, containing numbers indexing into image.pal.
}
[2] = Second frame
...
}
If specified, "palette file" must point to a file containing a serialised table, containing sub-tables with RGB values. Other elements can be present - see this example. The image will be re-mapped to use this palette. The result cannot be drawn / animated / converted to paintutils format by this API; omit the palette string to get images compatible with ComputerCraft terminals.
GIF.drawGIF(table imageData [, number xPos] [, number yPos] [, table terminal])
Draws a single image from a GIF that has been loaded by GIF.loadGIF(). You can either pass in a certain frame (eg myImage[4] for the fourth frame), or just pass in the whole GIF object to render just the first frame (eg myImage). In the case of any GIF that doesn't animate (ei it only contains a single image), just go with the latter.
If not specified, xPos and yPos default to 1, and "terminal" defaults to term.
GIF.animateGIF(table imageData [, number xPos] [, number yPos] [, table terminal])
Pretty much exactly the same as GIF.drawGIF(), but you can't hand it an individual frame - it requires the whole imageData table, and draws the contained frames in a loop. Doesn't stop on its own, so you'll need to rig up something via eg the parallel API if you only want to run it for a while.
GIF.saveGIF(table imageData, string fileName)
Saves the image to the specified file. As with GIF.drawGIF(), you can either hand in the whole GIF object or just a single frame (depending on whether you want to create an animating GIF or a static one - single-frame images are saved as static regardless). Also handles images loaded using paintutils.loadImage().
GIF.flattenGIF(table imageData [, boolean optimise]) => table imageData
Think of an animated GIF as being like an image with many invisible layers - at the beginning you can only see the first, but as each frame / layer's timer expires, the next is revealed on top of it, progressing the animation. Frames tend to only contain the part of the overall image they're affecting, but this function flattens each frame so that they each contain ALL the image data that should be visible when they're rendered. Rendering will be slower, but it's handy if you wish to eg bounce an animation around your screen.
If the "optimise" flag is set to true, it does the reverse, chopping flattened layers down to only contain the bare minimum content they need. GIFs are not automatically "optimised" in this way when loaded / resized, and the process can be time-intensive, so consider using GIF.saveGIF() after optimising so that you don't need to repeat it.
GIF.toPaintutils(table imageData) => table paintutils image
Returns a table suitable for use with paintutils.drawImage(), as though the frame had been paintutils.loadImage()'d. As with most other commands in the API, you can either hand in the whole GIF object or just a single frame. In the former case, the first frame of the image will be the one returned.
GIF.buildGIF(table paintutils image, table paintutils image, …) => table imageData
Takes any number of paintutils.loadImage()'d images and returns them as a GIF object, as if they were a GIF that'd been GIF.loadGIF()'d. Each frame will have a default delay of 0.1 set.
GIF.setBackgroundColour(table imageData, number / string colour)
Alters the GIF's background colour (used for transparent pixels when animating). Can either use a suitable number (eg colours.red) or a suitable paintutils string(eg, "e" for red).
When loading a GIF, the API checks to see which colours it doesn't make use of, and defaults to the first of these as the transparent colour. If a GIF uses all 16 of ComputerCraft's colours then black is the default.
Doesn't return anything, but rather just alters the original object. Yes, you can spell it wrong, if you must.
GIF.resizeGIF(table imageData [, number width] [, number height]) => table imageData
Returns a scaled version of a loaded GIF. Width or height can be omitted (set to nil, in the case of width), but not both - if one isn't specified, the other will be scaled to match.
For eg, if you wanted to alter a GIF to best match your display resolution, you might do:
Resize example
os.loadAPI("GIF")
local image = GIF.loadGIF("someImage.gif")
local mon = peripheral.find("monitor")
mon.setTextScale(0.5)
term.redirect(mon)
local x, y = term.getSize()
if x < image.width or y < image.height then
if x < math.floor(y / image.height * image.width) then
image = GIF.resizeGIF(image, x)
else
image = GIF.resizeGIF(image, nil, y)
end
end
GIF.drawGIF(image)
Consider GIF.saveGIF()'ing the result so you need not repeat this process. Using an external image editor can be much faster, of course.
Here's the sample script used in the video:
Example
os.loadAPI("GIF")
local mon = peripheral.find("monitor")
local gifs = fs.find("*.gif") -- Note: fs.find() is case-sensitive, even if your file system isn't.
mon.setTextScale(0.5)
local x, y = mon.getSize()
while true do for i = 1, #gifs do
local image = GIF.loadGIF(gifs[i])
mon.setBackgroundColour(image[1].transparentCol or image.backgroundCol)
mon.clear()
parallel.waitForAny(
function()
GIF.animateGIF(image, math.floor((x - image.width) / 2) + 1, math.floor((y - image.height) / 2) + 1, mon)
end,
function()
sleep(10)
end
)
end end
Version History
2015/04/271.0.0
Initial release.
1.0.1 - 1.0.3
Attempts to fix the jump between the final frame of animations back to the first. 1.0.3 seems to've nailed it.
2015/05/02
1.0.4
Some animating GIFs fail to set a frame rate; for those, a default is now set at 10fps.
2015/05/04
1.1.0
Added GIF.saveGIF() and GIF.toPaintutils().
2015/05/13
1.1.1
Flicker reduction.
Under older ComputerCraft builds, rendering is now faster and can no longer trigger yield protection.
2015/05/30
1.2.0
Further speed enhancements and bug fixes. Loading's slower, rendering's faster.
Backwards compatibility now extends to at least ComputerCraft 1.5, and probably further (previously stopped at 1.6).
Added GIF.flattenGIF(), GIF.resizeGIF(), GIF.setBackgroundColour() and GIF.buildGIF().
Individual image frames no longer have "transparentCol" keys; main image's "backgroundCol" key is used instead.
2015/09/26
1.2.1
Loading's faster (skimps on the optimising), rendering's pretty much the same speed anyway.
Can now pass additional filenames to GIF.loadGIF(), containing colour info - it'll map the loaded GIF to a palette defined within them.
2015/10/15
1.2.2
Fixes for GIF.buildGIF().
2015/12/21
1.2.3
First row of images produced with GIF.toPaintutils() will now always be the length of the original row width.
Fixes for GIF.flattenGIF().
2016/02/16
1.2.4
Fixes a floating-point issue affecting encoding under older CC builds.
2016/03/27
1.2.5
Fixes a crash bug affecting files with entirely clear frames.