#!/usr/local/bin/python # # $Id: pack-components,v 1.1 2001/11/05 20:43:30 shaper Exp $ # # A script to pack individual animation sequence frames for a set of # components into a single large image for each action sequence for # each component. The resulting images are added to a zip archive to # facilitate transmission from one place to another. # # The animation frames are expected to be stored one per file, and # represent each component viewed in each of the 8 cardinal compass # directions (N, NE, E, SE, S, SW, W, NW) for each action sequence. # Current action sequences are "standing" and "walking". # # For now, animation frame image file names are required to conform to # the following format: # # M__Cam0[1-8]_000<0-6>.png # ^ ^ # | | # orientation frame # index # # The standing action sequence has only 1 frame of animation for each # orientation. The image is expected to be in the file for frame # index 0. # # The walking action sequence has 6 frames of animation for each # orientation. The images are expected to start at frame index 1, # with other frames following. # # Accordingly, there should be a total of 56 image files for each # component (8 frames for standing, and 48 frames for walking.) # # 11/02/01 - Walter Korman (shaper@waywardgeeks.org) # import sys, os, re, time, Image, zipfile # # Constants. # frameWid = frameHei = 94 numOrients = 8 numFrames = 7 dirTmp = "/tmp" # our application-specific error class class Error(Exception): pass # # Returns a tuple detailing the given file's orientation index and # frame index, or None if an error occurs. # def getFileInfo (file): # get the frame orientation match = re.search("_Cam(\d+)_", file) if not match: return None # orientation index is 1-based in file name orient = int(match.group(1)) - 1 # get the frame index match = re.search("(\d+)\.png", file) if not match: return None frameidx = int(match.group(1)) return [orient, frameidx] # # Creates the temporary working directory used for data file storage, # or raises an error if the directory cannot be created. # def createTempDirectory (): if not os.path.exists(dirTmp): os.mkdir(dirTmp) if not os.path.isfile(dirTmp): raise OSError("Can't create temp directory [dir=%s]." % dirTmp) # # Returns a copy of the given image cropped to include only the # frame of the specified dimensions, which is assumed to be # centered within the source image. # def cropToFrame (im): size = im.getbbox() x = (size[2] - frameWid) / 2 y = (size[3] - frameHei) / 2 return im.crop([x, y, (x + frameWid), (y + frameHei)]) # # Returns the expected file name for the image file corresponding to # the given component file id, orientation and frame index. # def getSourceImageFile (fileid, orient, frameidx): return "M_%s_Cam0%d_000%d.png" % (fileid, (orient + 1), frameidx) # # Returns the file name for the image file containing all frames of # animation for the given action and component file id. # def getActionFile (action, fileid): return "%s/%s_%s.png" % (dirTmp, action, fileid) # # Returns a file name for an archive of component images. The # current time and date is embedded in the archive name. # def getArchiveFile (): suffix = time.strftime("%Y%m%d_%H%M", time.localtime()) return "%s/components_%s.zip" % (dirTmp, suffix) # # Creates a single large image containing all frames of animation for # the given action and file id and writes the resulting image to a # file. # def createActionImage (frameCount, startIndex, action, fileid, frames): # create the blank image imgwid = (frameWid * frameCount) imghei = (frameHei * numOrients) composite = Image.new("RGBA", [imgwid, imghei]) # slap all animation frames into the full image destx = desty = 0 for i in range(numOrients): for j in range(frameCount): composite.paste(frames[i][startIndex + j], (destx, desty)) destx += frameWid destx = 0 desty += frameHei # write out the full image imgFile = getActionFile(action, fileid) apply(composite.save, (imgFile,), { 'optimize': 1 }) # # Creates a zip archive for the given list of component file ids. # Assumes the action sequence images for the components have already # been created. # def createArchive (files): archiveFile = getArchiveFile() print "Creating archive [file=%s]" % archiveFile archive = zipfile.ZipFile(archiveFile, 'w', zipfile.ZIP_DEFLATED) for fileid in files: archive.write(getActionFile("standing", fileid)) archive.write(getActionFile("walking", fileid)) archive.close() # # Creates the full animation sequence images for the given component # file id and writes them to disk in preparation for archiving. # def packComponent (fileid): print "Packing '" + fileid + "'..." # initialize the frames frames = {} for i in range(numOrients): frames[i] = {} for j in range(numFrames): file = getSourceImageFile(fileid, i, j) if not os.path.exists(file): raise Error("Missing animation frame [file=%s]." % file) # get the file's orientation and frame index info = getFileInfo(file) if not info: raise Error("Can't get file animation info [file=%s]." % file) [orient, frameidx] = info # get a handle on the image object im = Image.open(file) # crop the image frames[orient][frameidx] = cropToFrame(im) # generate the full standing image createActionImage(1, 0, "standing", fileid, frames) # generate the full walking image createActionImage(numFrames - 1, 1, "walking", fileid, frames) # # Main program # if __name__ == "__main__": files = sys.argv[1:] if not files: print "Usage: pack-component fileid [fileid2 fileid3 ...]" sys.exit(1) # make sure the temporary file directory exists createTempDirectory() try : # create full animation sequence images for all components for fileid in files: packComponent(fileid) # package them all up createArchive(files) print "Done." except Error, e: print "Error: %s" % e