Script for making animated gifs

For discussion directly related to ConwayLife.com, such as requesting changes to how the forums or wiki function.
Post Reply
User avatar
codeholic
Moderator
Posts: 1142
Joined: September 13th, 2011, 8:23 am
Location: Hamburg, Germany

Script for making animated gifs

Post by codeholic » March 13th, 2015, 6:06 pm

Mostly @Flipper77: which script did you use to make an animated gif of the pufferfish? Would you share it please?
Ivan Fomichev

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Script for making animated gifs

Post by flipper77 » March 13th, 2015, 6:37 pm

I wish I had a script to do such a thing, all I'm doing is using an online gif maker to make them using "prt sc" for each frame, which isn't an ideal way to do it, since it takes a lot of time to do it, there's one in the golly scripts thread, but I doesn't have gridlines, which is why I'm not using that.

User avatar
codeholic
Moderator
Posts: 1142
Joined: September 13th, 2011, 8:23 am
Location: Hamburg, Germany

Re: Script for making animated gifs

Post by codeholic » March 13th, 2015, 6:41 pm

Would you please give a link? Adding gridlines should not be that hard.
Ivan Fomichev

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Script for making animated gifs

Post by flipper77 » March 13th, 2015, 6:44 pm

Here it is, Scorbie made it based off the perl version: viewtopic.php?f=9&t=45&start=100#p17015

User avatar
Scorbie
Posts: 1443
Joined: December 7th, 2013, 1:05 am

Re: Script for making animated gifs

Post by Scorbie » March 13th, 2015, 8:35 pm

There's a URL link in the script that could help you when you're stuck.
Velcrorex's avatar seems to deal well with moving objects. Maybe he might have something too.
By the way, how did people make all those gifs in LifeWiki? A script? Is there a source?
Best wishes to you, Scorbie

User avatar
velcrorex
Posts: 339
Joined: November 1st, 2009, 1:33 pm

Re: Script for making animated gifs

Post by velcrorex » March 13th, 2015, 9:40 pm

I took the animated gif from the lifewiki page on the loafer and resized it using an online service so the file would be small enough to use for my avatar.
-Josh Ball.

User avatar
calcyman
Posts: 2118
Joined: June 1st, 2009, 4:32 pm

Re: Script for making animated gifs

Post by calcyman » March 13th, 2015, 10:24 pm

I opted for animated SVGs for Catagolue since they're inline, scalable and efficient.

If your object fits into a 40-by-40 box (and is an oscillator or spaceship), the easiest way to retrieve it is to run canonise() and look at the corresponding object page, such as:

http://catagolue.appspot.com/object/xq7 ... 6952/b3s23

You can then click 'view source' and embed the HTML (demarcated by the svg tags) into any webpage.

I suppose I could create a Golly script to perform a similar function if necessary.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Extrementhusiast
Posts: 1812
Joined: June 16th, 2009, 11:24 pm
Location: USA

Re: Script for making animated gifs

Post by Extrementhusiast » March 13th, 2015, 11:22 pm

Scorbie wrote:By the way, how did people make all those gifs in LifeWiki? A script? Is there a source?
Those, including mine, were made during the time that conwaylife.com had a Java applet. The applet had contained an option to save a pattern animation as a GIF.
I Like My Heisenburps! (and others)

User avatar
codeholic
Moderator
Posts: 1142
Joined: September 13th, 2011, 8:23 am
Location: Hamburg, Germany

Re: Script for making animated gifs

Post by codeholic » March 14th, 2015, 3:35 am

calcyman wrote:I opted for animated SVGs for Catagolue since they're inline, scalable and efficient.
It is actually a very nice idea. I guess, generating SVG must be easier than making animated GIFs. I will try to restyle it so than it has the same look as other uploaded patterns on LifeWiki (i. e. black-on-white with gray gridlines). Then we just need to ask Nathaniel to allow uploading of SVG on the wiki.
Ivan Fomichev

User avatar
Scorbie
Posts: 1443
Joined: December 7th, 2013, 1:05 am

Re: Script for making animated gifs

Post by Scorbie » March 14th, 2015, 10:33 am

codeholic wrote:Then we just need to ask Nathaniel to allow uploading of SVG on the wiki.
And possibly our avatars.

On the other hand, I wonder if I could get a source code of the gif part of the java applet. Maybe we could possibly translate it into python. (Mostly because IE doesn't support SVG animation. Darn!)

Oh and one more thing. Is it possible to display the pattern animation/glider synth/rle etc on the same window of the pattern page or a small popup dialog? I think that's MUCH better than clicking a link to the rle and going back again to browse other patterns.
Best wishes to you, Scorbie

User avatar
Scorbie
Posts: 1443
Joined: December 7th, 2013, 1:05 am

Re: Script for making animated gifs

Post by Scorbie » March 15th, 2015, 2:41 am

I proudly present the new version of the giffer.py with support of gridlines and moving objects.
EDIT: The signs of the velocity follows the golly convention.
EDIT2: Changed the grid width to 2px and gray color to C6C6C6(LifeWiki Gray) as flipper77's suggestion.

Code: Select all

# Runs the current selection for a given number of steps and
# creates a black and white animated GIF file.
# Based on giffer.pl, which is based on code by Tony Smith.

import golly as g
import os
import struct

rect=g.getselrect()
if len(rect)==0:
    g.exit("Nothing in selection.")
[x,y,width,height]=rect
if(width>=65536 or height>=65536):
    g.exit("The width or height of the GIF file must be less than 65536 pixels.")

frames = pause = cellsize = gridwidth = vx = vy = filename = "?"
gridwidth = "2"
def getparam(msg):
    info = "Current parameters:\n"\
        +"The number of frames: "+frames+"\n"\
        +"The pause time of each frame: "+pause+"centisecs\n"\
        +"The size of each cell: "+cellsize+"px\n"\
        +"(The width of gridlines: "+gridwidth+"px -- fixed)\n"\
        +"The speed of the pattern: "+"("+vx+","+vy+")px/frame\n"\
        +"The file name: "+filename+"\n\n"
    return g.getstring(info+msg, "", "Create animated GIF")
frames = getparam("Enter the number of frames.")          
pause = getparam("Enter the pause time of each frame (in centisecs.)")
cellsize = getparam("Enter the size of each cell (in pixels.)")
# GridLines are 2 pixels in LifeWiki gif format.
# gridwidth = getparam("Enter the width of gridlines (in pixels, 0 to disable.)")
vx, vy = getparam("Enter the speed of the pattern (in pixels per frame.)\n ex)(-3,5)px/frame -> -3 5").split()
filename = getparam("Enter the file name. ex)out.gif")
    
def tryint(var, name):
    try:
        return int(var)
    except:
        g.exit(name + " is not an integer: " + var)
frames = tryint(frames, "Number of frames")
pause = tryint(pause, "Pause time")
cellsize = tryint(cellsize, "Cell Size")
gridwidth = tryint(gridwidth, "Grid Width")
vx = tryint(vx, "X velocity")
vy = tryint(vy, "Y velocity")
canvasheight = (cellsize+gridwidth)*height+gridwidth
canvaswidth = (cellsize+gridwidth)*width+gridwidth
if(canvaswidth>=65536 or canvasheight>=65536):
    g.exit("The width or height of the GIF file must be less than 65536 pixels."
            + "Width: " + canvaswidth + "Height: " + canvasheight)
# ------------------------------------------------------------------------------
def getpx(xrel, yrel, frameidx):
    xabs = xrel + vx * frameidx
    yabs = yrel + vy * frameidx
    if(xabs%(cellsize+gridwidth)<gridwidth or yabs%(cellsize+gridwidth)<gridwidth):
        return "2"
    else: return str(g.getcell(x+xabs/(cellsize+gridwidth), y+yabs/(cellsize+gridwidth)))

def getdata(frameidx):
    lines = []
    # Each array element is a line of 0 and 1 characters
    for ypx in xrange(canvasheight):
        line = ""
        for xpx in xrange(canvaswidth):
            line += getpx(xpx, ypx, frameidx)
        lines += line
    return lines
# ------------------------------------------------------------------------------
def compress(lines):
    table = {'0': 0, '1': 1, '2': 2, '3': 3}
    curr = cc = 4
    used = eoi = 5
    bits = size = 3
    mask = 7
    output = code = ""
    for line in lines:
        for i in xrange(len(line)):
            next = line[i]
            if (code + next) in table:
                code += next
            else:
                used += 1
                table[(code + next)] = used
                curr += table[code] << bits
                bits += size
                while(bits >= 8):
                    output += chr(curr & 255)
                    curr >>= 8
                    bits -= 8
                if(used > mask):
                    if(size < 12):
                        size += 1
                        mask = mask*2 + 1
                    else:
                        curr += cc << bits # output cc in current width
                        bits += size
                        while(bits >= 8):
                            output += chr(curr & 255)
                            curr >>= 8
                            bits -= 8
                        table = {'0': 0, '1': 1} #reset table
                        used = 5
                        bits = 3
                        mask = 7
                code = next
    curr += table[code] << bits
    bits += size
    while(bits >= 8):
        output += chr(curr & 255)
        curr >>= 8
        bits -= 8
    output += chr(curr)
    subbed = ""
    while(len(output) > 255):
        subbed += chr(255) + output[:255]
        output = output[255:]
    return subbed + chr(len(output)) + output + chr(0)
# ----------------------------------------------------------------------
# GIF formatting
# Useful information of GIF formats in:
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
# ----------------------------------------------------------------------
header = "GIF89a"
screendesc = struct.pack("<2HB2b", canvaswidth, canvasheight, 0x91, 0, 0)
# Colors in colortable: White, Black, LifeWiki Gray, and Black(Unused) (3 bytes each)
colortable = "\xFF\xFF\xFF\x00\x00\x00\xC6\xC6\xC6\x00\x00\x00"
applic = "\x21\xFF\x0B" + "NETSCAPE2.0" + struct.pack("<2bHb", 3, 1, 0, 0)
imagedesc = struct.pack("<4HB", 0, 0, canvaswidth, canvasheight, 0x00)

try:
    gif = open(os.path.join(os.getcwd(), filename),"wb")
except:
    g.exit("Unable to open file.")

gif.write(header + screendesc + colortable + applic)
for f in xrange(frames):
    # Graphics control extension
    gif.write("\x21\xF9" + struct.pack("<bBH2b", 4, 0x00, pause, 0, 0))
    # Get data for this frame
    gif.write("," + imagedesc + chr(2) + compress(getdata(f)))
    g.show(str(f+1)+"/"+str(frames))
    if(f+1 < frames):
        g.run(1)
        g.update()
gif.close()
g.show("GIF animation saved in " + filename)
Attachments
LifeWiki Syntax, 0.32s interval<br />I'm not sure what causes the choppy delays in chrome.<br />If you rightclick -&gt;view image in new tab,<br />You can see that the gif works well. It's a mystery...
LifeWiki Syntax, 0.32s interval
I'm not sure what causes the choppy delays in chrome.
If you rightclick ->view image in new tab,
You can see that the gif works well. It's a mystery...
myosc.gif (7.36 KiB) Viewed 13567 times
Last edited by Scorbie on March 15th, 2015, 8:36 am, edited 8 times in total.
Best wishes to you, Scorbie

User avatar
Scorbie
Posts: 1443
Joined: December 7th, 2013, 1:05 am

Re: Script for making animated gifs

Post by Scorbie » March 15th, 2015, 3:21 am

calcyman wrote:I opted for animated SVGs for Catagolue since they're inline, scalable and efficient.
codeholic wrote:I will try to restyle it so than it has the same look as other uploaded patterns on LifeWiki (i. e. black-on-white with gray gridlines).
Still, I think SVG making scripts are worth publishing, because it's scalable, and because I just found out that gifs don't work so nicely on some browsers like chrome and smartphones.
Best wishes to you, Scorbie

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Script for making animated gifs

Post by flipper77 » March 15th, 2015, 3:35 am

Scorbie wrote: Still, I think SVG making scripts are worth publishing, because it's scalable, and because I just found out that gifs don't work so nicely on some browsers like chrome and smartphones.
I use chrome pretty much exclusively, and the only gifs that have trouble showing properly are the ones that have short delays between frames. Anyways, the script is great, but a few things I want to mention:
  • Grid lines should be 2 pixels wide with a color of #C6C6C6 to match other images on LifeWiki
  • Spaceship gifs are a bit choppy with delays that fit LifeWiki's other ones
I may mess around with the script to get smoother gifs for spaceships, since there are some that lack one at this time.

User avatar
Scorbie
Posts: 1443
Joined: December 7th, 2013, 1:05 am

Re: Script for making animated gifs

Post by Scorbie » March 15th, 2015, 6:35 am

flipper77 wrote:Grid lines should be 2 pixels wide with a color of #C6C6C6 to match other images on LifeWiki
Thanks, that seems easy enough to change.
flipper77 wrote:Spaceship gifs are a bit choppy with delays that fit LifeWiki's other ones
I would have to study more about gifs to figure that out. --Which might take some time.
EDIT: That's strange. When I open the gif file in my local computer -- with chrome-- there aren't any problems.

Well, I think SVGs don't have choppy delays, which is a good plus.
Best wishes to you, Scorbie

flipper77
Posts: 197
Joined: October 24th, 2010, 3:25 am
Location: Spokane, WA

Re: Script for making animated gifs

Post by flipper77 » March 15th, 2015, 7:20 am

Well, I've modified Scorbies script to allow for smoother spaceship gifs:

Code: Select all

# Runs the current selection for a given number of steps and
# creates a black and white animated GIF file.
# Based on giffer.pl, which is based on code by Tony Smith.

import golly as g
import os
import struct

rect=g.getselrect()
if len(rect)==0:
    g.exit("Nothing in selection.")
[x,y,width,height]=rect
if(width>=65536 or height>=65536):
    g.exit("The width or height of the GIF file must be less than 65536 pixels.")

gens = pause = cellsize = gridwidth = vx = vy = fpg = filename = "?"
def getparam(msg, init = ""):
    info = "Current parameters:\n"\
        +"The number of gens: "+gens+"\n"\
        +"The pause time of each frame: "+pause+"centisecs\n"\
        +"The size of each cell: "+cellsize+"px\n"\
        +"The width of gridlines: "+gridwidth+"px\n"\
        +"The speed of the pattern: "+"("+vx+","+vy+")cells/frame\n"\
        +"The number of frames per gen: "+fpg+" per gen\n"\
        +"The file name: "+filename+"\n\n"
    return g.getstring(info+msg, init, "Create animated GIF")
gens = getparam("Enter the number of gens.", "4")          
pause = getparam("Enter the pause time of each frame (in centisecs.)", "50")
cellsize = getparam("Enter the size of each cell (in pixels.)", "14")
gridwidth = getparam("Enter the width of gridlines (in pixels, 0 to disable.)", "2")
vx, vy = getparam("Enter the speed of the pattern (in cells per frame.)\n ex)(-3,5)cells/frame -> -3 5", "0 0").split()
fpg = getparam("Enter the frames per gen(1 for oscillators).", "4")
filename = getparam("Enter the file name. ex)out.gif", "out.gif")
getparam("Press OK to continue.")
    
def tryint(var, name):
    try:
        return int(var)
    except:
        g.exit(name + " is not an integer: " + var)


gens = tryint(gens, "Number of gens")
pause = tryint(pause, "Pause time")
cellsize = tryint(cellsize, "Cell Size")
gridwidth = tryint(gridwidth, "Grid Width")
vx = tryint(vx, "X velocity")
vy = tryint(vy, "Y velocity")
fpg = tryint(fpg, "Frames/gen")
canvasheight = (cellsize+gridwidth)*height+gridwidth
canvaswidth = (cellsize+gridwidth)*width+gridwidth

if (cellsize+gridwidth)%(fpg*gens) != 0:
    g.exit("%s won't be smooth: (%d + %d)/(%d * %d) isn't an integer." % (filename, cellsize, gridwidth, fpg, gens))
else:
    modifier = (cellsize+gridwidth)/(fpg*gens)

if(canvaswidth>=65536 or canvasheight>=65536):
    g.exit("The width or height of the GIF file must be less than 65536 pixels."
            + "Width: " + canvaswidth + "Height: " + canvasheight)
# ------------------------------------------------------------------------------
def getpx(xrel, yrel, frameidx):
    xabs = xrel + vx * frameidx * modifier
    yabs = yrel + vy * frameidx * modifier
    if(xabs%(cellsize+gridwidth)<gridwidth or yabs%(cellsize+gridwidth)<gridwidth):
        return "2"
    else: return str(g.getcell(x+xabs/(cellsize+gridwidth), y+yabs/(cellsize+gridwidth)))

def getdata(frameidx):
    lines = []
    # Each array element is a line of 0 and 1 characters
    for ypx in xrange(canvasheight):
        line = ""
        for xpx in xrange(canvaswidth):
            line += getpx(xpx, ypx, frameidx)
        lines += line
    return lines
# ------------------------------------------------------------------------------
def compress(lines):
    table = {'0': 0, '1': 1, '2': 2, '3': 3}
    curr = cc = 4
    used = eoi = 5
    bits = size = 3
    mask = 7
    output = code = ""
    for line in lines:
        for i in xrange(len(line)):
            next = line[i]
            if (code + next) in table:
                code += next
            else:
                used += 1
                table[(code + next)] = used
                curr += table[code] << bits
                bits += size
                while(bits >= 8):
                    output += chr(curr & 255)
                    curr >>= 8
                    bits -= 8
                if(used > mask):
                    if(size < 12):
                        size += 1
                        mask = mask*2 + 1
                    else:
                        curr += cc << bits # output cc in current width
                        bits += size
                        while(bits >= 8):
                            output += chr(curr & 255)
                            curr >>= 8
                            bits -= 8
                        table = {'0': 0, '1': 1} #reset table
                        used = 5
                        bits = 3
                        mask = 7
                code = next
    curr += table[code] << bits
    bits += size
    while(bits >= 8):
        output += chr(curr & 255)
        curr >>= 8
        bits -= 8
    output += chr(curr)
    subbed = ""
    while(len(output) > 255):
        subbed += chr(255) + output[:255]
        output = output[255:]
    return subbed + chr(len(output)) + output + chr(0)
# ----------------------------------------------------------------------
# GIF formatting
# Useful information of GIF formats in:
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
# ----------------------------------------------------------------------
header = "GIF89a"
screendesc = struct.pack("<2HB2b", canvaswidth, canvasheight, 0x91, 0, 0)
# Colors in colortable: White, Black, Gray, and Black(Unused) (3 bytes each)
colortable = "\xFF\xFF\xFF\x00\x00\x00\xC6\xC6\xC6\x00\x00\x00"
applic = "\x21\xFF\x0B" + "NETSCAPE2.0" + struct.pack("<2bHb", 3, 1, 0, 0)
imagedesc = struct.pack("<4HB", 0, 0, canvaswidth, canvasheight, 0x00)

try:
    gif = open(os.path.join(os.getcwd(), filename),"wb")
except:
    g.exit("Unable to open file.")

gif.write(header + screendesc + colortable + applic)
for f in xrange(gens*fpg):
    # Graphics control extension
    gif.write("\x21\xF9" + struct.pack("<bBH2b", 4, 0x00, pause, 0, 0))
    # Get data for this frame
    gif.write("," + imagedesc + chr(2) + compress(getdata(f)))
    g.show(str(f+1)+"/"+str(gens*fpg))
    if ((fpg == 1) | (f % fpg == fpg - 1)):
        g.run(1)
        g.update()
gif.close()
g.show("GIF animation saved in " + filename)
I used the code before his last edit, so you can still modify gridwidth if you like, most animations on LifeWiki have 2 pixel wide gridlines, but some look better with other options. Test it on a glider and compare to LifeWikis version to see how they compare.

EDIT: The code that wants smooth gifs is only there to show that the output gif may not be perfectly smooth, but that's hard to avoid if said gif is a spaceship type. A small bit of the code can be changed to make it better suited.

EDIT2: For some reason, the script I posted has problems with patterns with several frames, hopefully someone can find the problem.

User avatar
codeholic
Moderator
Posts: 1142
Joined: September 13th, 2011, 8:23 am
Location: Hamburg, Germany

Re: Script for making animated gifs

Post by codeholic » March 27th, 2015, 5:40 pm

After I fixed a small glitch it works pretty fine for me: https://github.com/conwaylife/giffer

Thanks to all who participated.
Ivan Fomichev

Namesnipe
Posts: 1
Joined: May 7th, 2015, 3:54 am

Re: Script for making animated gifs

Post by Namesnipe » May 7th, 2015, 3:56 am

Whoopity-doo, I sniped myself a name!

User avatar
Freywa
Posts: 620
Joined: June 23rd, 2011, 3:20 am
Location: Singapore
Contact:

Re: Script for making animated gifs

Post by Freywa » May 7th, 2015, 7:34 am

Namesnipe wrote:Whoopity-doo, I sniped myself a name!
What are you doing here? Is it related to cellular automata?
Princess of Science, Parcly Taxel

Post Reply