From bb0313632bed80893f0adb808d14839383ded711 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Mon, 20 Apr 2015 15:26:03 -0500 Subject: [PATCH 1/3] Added LICEcap's LCF file format support --- anim_encoder.py | 28 +++++++----- lcf.py | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 lcf.py diff --git a/anim_encoder.py b/anim_encoder.py index 0deae29..8255bbf 100755 --- a/anim_encoder.py +++ b/anim_encoder.py @@ -128,15 +128,23 @@ def find_matching_rect(bitmap, num_used_rows, packed, src, sx, sy, w, h): return None def generate_animation(anim_name): - frames = [] - rex = re.compile("screen_([0-9]+).png") - for f in os.listdir(anim_name): - m = re.search(rex, f) - if m: - frames.append((int(m.group(1)), anim_name + "/" + f)) - frames.sort() - - images = [misc.imread(f) for t, f in frames] + if anim_name.endswith('.lcf'): + from lcf import process + frames = [(f.delta, f) for f in process(anim_name)] + images = [f.get_array() for t, f in frames] + delays = [t for t, f in frames] + [END_FRAME_PAUSE] + anim_name = anim_name[:-4] + else: + frames = [] + rex = re.compile("screen_([0-9]+).png") + for f in os.listdir(anim_name): + m = re.search(rex, f) + if m: + frames.append((int(m.group(1)), anim_name + "/" + f)) + frames.sort() + images = [misc.imread(f) for t, f in frames] + times = [t for t, f in frames] + delays = (array(times[1:] + [times[-1] + END_FRAME_PAUSE]) - array(times)).tolist() zero = images[0] - images[0] pairs = zip([zero] + images[:-1], images) @@ -204,8 +212,6 @@ def generate_animation(anim_name): os.system("mv " + anim_name + "_packed_tmp.png " + anim_name + "_packed.png") # Generate JSON to represent the data - times = [t for t, f in frames] - delays = (array(times[1:] + [times[-1] + END_FRAME_PAUSE]) - array(times)).tolist() timeline = [] for i in xrange(len(images)): diff --git a/lcf.py b/lcf.py new file mode 100644 index 0000000..8c52185 --- /dev/null +++ b/lcf.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +from __future__ import division + +import zlib +import struct +from collections import namedtuple + +import numpy as np + + +LCF_VERSION = 0x11CEb001 + +LcfHeader = namedtuple('LcfHeader', 'version bpp w h bsize_w bsize_h nf cdata_left dsize') +LcfBlock = namedtuple('LcfBlock', 'hdr frame_deltas frames') + + +class LcfFrame(object): + def __init__(self, frame, block, idx): + self.frame = frame + self.block = block + self.idx = idx + self.delta = block.frame_deltas[idx] + + def get_array(self): + hdr = self.block.hdr + arr = np.ndarray(shape=(0, hdr.w, 3), dtype=np.uint8) + for y, ypos in enumerate(xrange(0, hdr.h, hdr.bsize_h)): + hei = min(hdr.h - ypos, hdr.bsize_h) + line = np.ndarray(shape=(hei, 0, 3), dtype=np.uint8) + for x, xpos in enumerate(xrange(0, hdr.w, hdr.bsize_w)): + # wid = min(hdr.w - xpos, hdr.bsize_w) + line = np.concatenate((line, self.frame[x][y]), axis=1) + arr = np.concatenate((arr, line), axis=0) + return arr + + +class BadBlock(Exception): + pass + + +class CorruptedBlock(BadBlock): + pass + + +def read(fd, size): + data = fd.read(size) + if len(data) != size: + raise BadBlock("Cannot read %s bytes from file" % size) + return data + + +def read_block(fd): + hdr = LcfHeader(*struct.unpack('iiiiiiiii', read(fd, 9 * 4))) + if hdr.version != LCF_VERSION: + raise BadBlock("Wrong LCF version") + if hdr.nf < 1 or hdr.nf > 1024: + raise BadBlock("Wrong number of frames") + if hdr.bpp != 16: + raise CorruptedBlock("Wrong bits per pixel") + frame_deltas = [struct.unpack('i', read(fd, 4))[0] for f in xrange(hdr.nf)] + cdata = read(fd, hdr.cdata_left) + decompress = zlib.decompressobj() + data = decompress.decompress(cdata) + if hdr.dsize != len(data): + raise CorruptedBlock("Wrong uncompressed data size") + # Decode slices + bytespersample = (hdr.bpp + 7) // 8 + # Setup frames as blocks of ns_x * ns_y slices + ns_x = (hdr.w + hdr.bsize_w - 1) // hdr.bsize_w + ns_y = (hdr.h + hdr.bsize_h - 1) // hdr.bsize_h + frames = [[[[] for y in xrange(ns_y)] for x in xrange(ns_x)] for f in xrange(hdr.nf)] + sp = 0 + for y, ypos in enumerate(xrange(0, hdr.h, hdr.bsize_h)): + hei = min(hdr.h - ypos, hdr.bsize_h) + for x, xpos in enumerate(xrange(0, hdr.w, hdr.bsize_w)): + wid = min(hdr.w - xpos, hdr.bsize_w) + sz1 = hei * wid * bytespersample + cnt = 0 + for i in xrange(hdr.nf): + if cnt > 0: + cnt -= 1 + else: + buff = data[sp:sp + sz1] + if len(buff) != sz1: + raise CorruptedBlock("Wrong size read!") + arr = np.fromstring(buff, dtype=np.uint16) + lvalid = np.zeros(arr.size * 3, np.uint8) + lvalid.view()[0::3] = (arr << 3) & 0xf8 + lvalid.view()[1::3] = (arr >> 3) & 0xfc + lvalid.view()[2::3] = (arr >> 8) & 0xf8 + lvalid.shape = (hei, wid, 3) + sp += sz1 + if i < hdr.nf - 1: + cnt = ord(data[sp]) + sp += 1 + frames[i][x][y] = lvalid + if sp != len(data): + print "Not all data consumed (%s/%s)!" % (sp, len(data)) + return LcfBlock(hdr, frame_deltas, frames) + + +def process(filename): + blocks = [] + with open(filename) as fd: + while True: + try: + blocks.append(read_block(fd)) + except CorruptedBlock as e: + print(e) + break + except BadBlock: + break + return [LcfFrame(f, b, i) for i, b in enumerate(blocks) for f in b.frames] From c9bcd25006fbcee475885ad3c21b1281facc3d92 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Thu, 23 Apr 2015 15:34:22 -0500 Subject: [PATCH 2/3] Fixed frame index --- lcf.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lcf.py b/lcf.py index 8c52185..0ef17e8 100644 --- a/lcf.py +++ b/lcf.py @@ -15,11 +15,12 @@ class LcfFrame(object): - def __init__(self, frame, block, idx): + def __init__(self, frame, block, block_idx, frame_idx): self.frame = frame self.block = block - self.idx = idx - self.delta = block.frame_deltas[idx] + self.block_idx = block_idx + self.frame_idx = frame_idx + self.delta = block.frame_deltas[frame_idx] def get_array(self): hdr = self.block.hdr @@ -110,4 +111,4 @@ def process(filename): break except BadBlock: break - return [LcfFrame(f, b, i) for i, b in enumerate(blocks) for f in b.frames] + return [LcfFrame(f, b, i, j) for i, b in enumerate(blocks) for j, f in enumerate(b.frames)] From fa6f4f741af02c2a6a1188854d1b8788d2c64d50 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Thu, 23 Apr 2015 15:52:08 -0500 Subject: [PATCH 3/3] Some times, frame delta is negative (for some weird unknown reason) --- lcf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lcf.py b/lcf.py index 0ef17e8..524c14f 100644 --- a/lcf.py +++ b/lcf.py @@ -20,7 +20,7 @@ def __init__(self, frame, block, block_idx, frame_idx): self.block = block self.block_idx = block_idx self.frame_idx = frame_idx - self.delta = block.frame_deltas[frame_idx] + self.delta = abs(block.frame_deltas[frame_idx]) def get_array(self): hdr = self.block.hdr