Skip to content

Commit

Permalink
Added LICEcap's LCF file format support
Browse files Browse the repository at this point in the history
  • Loading branch information
Kronuz committed Apr 21, 2015
1 parent 06f6fec commit bb03136
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 11 deletions.
28 changes: 17 additions & 11 deletions anim_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)):
Expand Down
113 changes: 113 additions & 0 deletions lcf.py
Original file line number Diff line number Diff line change
@@ -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]

0 comments on commit bb03136

Please sign in to comment.