1
# -*- coding: utf-8 -*-
3
# This file is part of emesene.
5
# Emesene is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# Emesene is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with emesene; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
class IsfBin( object ):
26
'''Base class to encoder and decoder, represents
27
the information read from or to be stored in
28
a binary Isf file, as well as the functions
29
necessary to do so '''
31
def __init__( self, data = '' ):
36
def pop( self, length = 1 ):
37
'''gets the next $len chars from the data stack,
38
and increment the pointer'''
41
end = self.pointer + length
42
self.pointer += length
44
return self.data[start:end]
46
def pops( self, format ):
47
'''Reads formatted data from the struct. Supports, in
48
in addition to anthing supported by struct, mbuint32/64,
50
if format.startswith('mbuint32'):
55
byte = struct.unpack( '<B', self.pop(1) )[0]
60
if byte > 0x7f and bitcount < 29:
66
elif format.startswith('mbuint64'):
71
byte = struct.unpack( '<B', self.pop(1) )[0]
76
if byte > 0x7f and bitcount < 57:
82
# The number of points in a stroke seems to use this strange
83
# format of integer. It is essentially a multi-byte integer
84
# where the least significant bits come first in the stream.
85
elif format.startswith('weirdoint'):
90
byte = struct.unpack( '<B', self.pop(1) )[0]
92
temp = temp + num * pow(2, bitcount)
100
elif format.startswith('mbsint32'):
101
temp = self.pops('mbuint64')
107
# pop struct: get size, pop(), unpack()
108
size = struct.calcsize(format)
109
return struct.unpack( format, self.pop(size) )
111
def push( self, newdata, insert = False ):
112
'''adds $newdata to the data stack, if insert is True then
113
it adds it to the beginning of the stack'''
115
self.data = newdata + self.data
119
def pushs( self, format, *vars ):
120
'''Pushes formatted data onto the struct. Supports, in
121
in addition to anthing supported by struct, mbuint32/64,
123
# TODO: I need a way to insert things at the beginning
124
# for now insert is always False
127
if format.startswith('mbuint32'):
129
print 'Only one value per', format
131
if value > 0xfffffff or value < 0:
132
print 'Value not within', format, 'range'
140
value = int(value / 2)
151
values.insert(0, num)
154
for val in values.reverse():
155
self.push( struct.pack( '<B', val, insert = True ) )
158
self.push( struct.pack( '<B', val ) )
160
elif format.startswith('mbuint64'):
162
print 'Only one value per', format
164
if value > 0xfffffffffffffff or value < 0:
165
print 'Value not within', format, 'range'
173
value = int(value / 2)
184
values.insert(0, num)
187
for val in values.reverse():
188
self.push( struct.pack( '<B', val, insert = True ) )
191
self.push( struct.pack( '<B', val ) )
193
elif format.startswith('mbsint32'):
195
print 'Only one value per', format
197
if abs(value) > 0xfffffff:
198
print 'Value not within', format, 'range'
204
value = value * 2 + sign
205
self.pushs('mbuint64', value)
207
elif format.startswith('weirdoint'):
209
print 'Only one value per', format
218
value = int(value / 2)
233
for val in values.reverse():
234
self.push( struct.pack( '<B', val, insert = True ) )
237
self.push( struct.pack( '<B', val ) )
240
# push struct: adds a packed struct to the data stack
241
self.push(struct.pack(format, *vars))
243
class ImageDescriptor( object ):
244
'''A class that represents a single image'''
245
# The following two arrays, taglist and tagformat are organized
246
# so the index is the tag's index, and the format strings are
247
# the type of payload following the tag
249
# Array of tag strings
251
'INK_SPACE_RECT', 'GUID_TABLE', 'DRAW_ATTRS_TABLE',
252
'DRAW_ATTRS_BLOCK', 'STROKE_DESC_TABLE', 'STROKE_DESC_BLOCK',
253
'BUTTONS', 'NO_X', 'NO_Y', 'DIDX', 'STROKE', 'STROKE_PROPERTY_LIST',
254
'POINT_PROPERTY', 'SIDX', 'COMPRESSION_HEADER', 'TRANSFORM_TABLE',
255
'TRANSFORM', 'TRANSFORM_ISOTROPIC_SCALE', 'TRANSFORM_ANISOTROPIC_SCALE',
256
'TRANSFORM_ROTATE', 'TRANSFORM_TRANSLATE', 'TRANSFORM_SCALE_AND_TRANSLATE',
257
'TRANSFORM_QUAD', 'TIDX', 'METRIC_TABLE', 'METRIC_BLOCK', 'MIDX',
258
'MANTISSA', 'PERSISTENT_FORMAT', 'HIMETRIC_SIZE', 'STROKE_IDS']
260
# Array of format stings; a V means an mbuint64 containing
261
# the payload size follows
263
'pass', 'V', 'V', 'V', 'V', 'V', 'pass', 'pass', 'pass', 'mbuint32', 'V',
264
'pass', 'pass', 'mbuint32', 'pass', 'V', '<ffffff', '<f', '<ff', 'mbuint32',
265
'<ff', '<ffff', '<IIIIII', 'mbuint32', 'V', 'V', 'pass', 'mbuint64', 'V',
268
# Array of bitamounts used in huffman decompression
270
[0, 1, 2, 4, 6, 8, 12, 16, 24, 32], [0, 1, 1, 2, 4, 8, 12, 16, 24, 32],
271
[0, 1, 1, 1, 2, 4, 8, 14, 22, 32], [0, 2, 2, 3, 5, 8, 12, 16, 24, 32],
272
[0, 3, 4, 5, 8, 12, 16, 24, 32], [0, 4, 6, 8, 12, 16, 24, 32],
273
[0, 6, 8, 12, 16, 24, 32], [0, 7, 8, 12, 16, 24, 32]]
275
def __init__( self ):
279
self.himetric_size = [0, 0]
286
self.draw_attrs_table = []
289
def print_info( self ):
290
'''Prints some information about the isf file'''
291
print 'ISFTag:', self.isf_tag
292
print 'ISFSize:', self.isf_size
293
print 'Image Size:', self.himetric_size[0], 'x', self.himetric_size[1]
295
for trans in self.transforms:
298
for path in self.paths:
300
print 'Indices:', self.indices
303
class IsfEncoder( ImageDescriptor ):
304
'''encodes an isf file'''
305
def __init__( self, paths, debug=False):
311
'''Fills the data array'''
312
for path in self.paths:
313
stroke = self.enc_stroke(path)
314
self.enc.pushs('mbuint32', taglist.index('STROKE'))
315
self.enc.pushs('mbuint64', len(stroke))
316
self.enc.data += stroke
317
self.enc.pointer += len(stroke)
320
def add_headers( self ):
321
'''Adds basic information to the top of the file'''
323
head.pushs('mbuint32', 0)
324
head.pushs('mbuint64', len(self.enc.data))
325
self.enc.data = head.data + self.enc.data
326
self.enc.pointer += len(head.data)
328
def enc_stroke(self, path, enc):
329
num_pts = len(path._points)
331
stroke.pushs('weirdoint', num_pts)
332
guid_x, guid_y = [], []
333
for point in path._points:
334
guid_x.append(point.x)
335
guid_y.append(point.y)
337
packet_x = enc_huffman(guid_x)
338
packet_y = enc_huffman(guid_y)
341
packet_x = enc_gorilla(guid_x)
342
packet_y = enc_gorilla(guid_y)
344
stroke.data += tag + packet_x + tag + packet_y
345
stroke.data = struct.pack('<B', len(stroke.data)) + stroke.data
348
def enc_huffman(self, guid):
350
# Initial point to the stream
351
enc_huffvalue(guid[0], stream)
356
curdelta = guid[i] - guid[i-1]
357
diff = curdelta - prevdelta
359
enc_huffvalue(diff, stream)
361
def enc_huffvalue(self, val, stream):
365
# Get the number of bits needed to represent
368
# Find the next smallest length in bitamounts
369
while not bitamounts[2].count(length):
371
# Get the index of that length
372
index = bitamounts[2].index(length)
373
# Push that index to the stream
374
stream.push_count(index)
375
# Push the actual value of length length
376
stream.push_int(val, length)
378
class transform( object ):
382
class IsfDecoder( ImageDescriptor ):
383
'''decodes an isf file'''
385
def __init__( self, data, debug=False):
386
ImageDescriptor.__init__( self )
388
# Dictionary of functions associated with tags
390
'STROKE':self.dec_stroke,
391
'DIDX':self.dec_index,
392
'SIDX':self.dec_index,
393
'TIDX':self.dec_index,
394
'MIDX':self.dec_index,
395
'DRAW_ATTRS_BLOCK':self.dec_dablock,
396
'DRAW_ATTRS_TABLE':self.dec_datable
403
def fill( self, dec ):
404
'''reads the data and fills each field of the file'''
406
self.isf_tag = dec.pops('mbuint32')
408
print 'ISFTag', self.isf_tag
409
self.isf_size = dec.pops('mbuint64')
411
print 'ISFSize', self.isf_size
415
tagnum = dec.pops('mbuint32')
416
tag = self.taglist[tagnum]
419
format = self.tagformat[tagnum]
422
print 'Uknown Tag Value'
434
payload_size = dec.pops('mbuint64')
436
print '\tPayload Size', payload_size
437
payload = dec.data[dec.pointer:dec.pointer+payload_size]
438
if self.tagdict.has_key(tag):
440
self.tagdict[tag](tag, payload)
441
if self.debug and prnt:
443
print '\t', hex(struct.unpack('<B', char)[0])
444
dec.pointer += payload_size
446
payload = dec.pops(format)
447
if self.tagdict.has_key(tag):
449
self.tagdict[tag](tag, payload)
450
if self.debug and prnt:
451
print '\tPayload', payload
453
def process_tag(self, tag, payload):
454
'''Performs specific operations with supported tags'''
455
if tag == 'STROKE_IDS':
456
dat = IsfBin(payload)
457
num_pts = dat.pops('mbuint32')
458
stream = Bitstream(payload[dat.pointer:])
459
ids = self.dec_packet(stream, num_pts)
462
elif tag == 'HIMETRIC_SIZE':
463
dat = IsfBin(payload)
464
width = dat.pops('mbsint32')
465
height = dat.pops('mbsint32')
466
self.himetric_size = [width, height]
469
def dec_datable( self, tag, payload ):
470
dat = IsfBin(payload)
474
block_size = dat.pops('mbuint32')
475
self.dec_dablock(tag, dat.data[dat.pointer:dat.pointer + block_size], index)
476
dat.pointer += block_size
479
print 'End of DRAW_ATTR_TABLE'
480
for table in self.draw_attrs_table:
481
print table.color.to_string()
487
def dec_dablock( self, tag, payload, index = 0 ):
488
dat = IsfBin(payload)
489
if index >= len(self.draw_attrs_table):
490
self.draw_attrs_table.append(drawing.DrawAttributes())
492
color = gtk.gdk.Color(0, 0, 0)
495
guid = dat.pops('mbuint32')
498
print 'End of DRAW_ATTR_BLOCK'
499
self.draw_attrs_table[index].color = color
504
print '\tGUID', hex(guid)
506
data = dat.pops('mbuint32')
507
print '\t\tData', hex(data)
509
data = dat.pops('<BBBB')
514
red = (red * 65535) / 127
515
green = (data & 0x3f80) / 0x80
516
#green = 0xff - green
517
green = (green * 65535) / 127
518
blue = (data & 0x1f6000) / 0x4000
520
blue = (blue * 65535) / 127
521
color = gtk.gdk.Color(green, red, blue)
523
def dec_index( self, tag, payload ):
524
self.indices[tag] = payload
525
print '\tIndices:', self.indices
527
def dec_stroke( self, tag, payload ):
528
'''Decodes stroke data from the payload, including
529
the number of data points, the x values, and the
531
# Get number of points
532
dat = IsfBin(payload)
533
num_pts = dat.pops('weirdoint')
534
num_bytes = dat.pointer
537
print '\tNumPoints:', num_pts
538
# Create the bit stream
539
stream = Bitstream( payload[num_bytes:] )
541
# Decode the two packets
542
guid_x = self.dec_packet(stream, num_pts)
543
guid_y = self.dec_packet(stream, num_pts)
544
if guid_x == -1 or guid_y == -1:
547
path = drawing.BrushPath(drawing.Point(guid_x[0], guid_y[0]),
548
self.draw_attrs_table[self.indices['DIDX']])
549
for i in range(1, len(guid_x)):
550
path.add(drawing.Point(guid_x[i], guid_y[i]))
551
self.paths.append(path)
553
def dec_packet(self, stream, num_pts):
554
'''Function that decodes *ISF/PACKET* formatted data from
555
a Bitstream class object, and the number of points
558
tag = stream.pop_byte()
559
if tag & 0xc0 == 0x80:
560
# Adaptive-Huffman Compression
562
print '\tAdaptive-Huffman Compression'
565
pts = self.dec_huffman(stream, index, num_pts)
572
# Gorilla Compression
574
print '\tGorilla Compression'
575
# TODO: Figure out what the gorilla transformation is
576
transformation = tag & 0x20
577
if transformation == 0x20:
578
print "\tError: Gorilla transformation unimplemented", hex(tag)
583
pts = self.dec_gorilla(stream, width, num_pts)
591
def dec_huffman( self, stream, index, num_pts ):
592
'''Decodes num_pts values from a Bistream object, using
593
bitamounts[index] to decode it using an Adaptive-Huffman
597
bitamount = self.bitamounts[index]
599
# Create huffbase array
602
for value in bitamount:
604
huffbase.append(base)
605
base += pow(2, value-1)
611
while len(value) < num_pts:
612
bit = stream.pop_bit()
618
elif n_count < len(bitamount):
619
offset = stream.pop_bits_to_int(bitamount[n_count])
622
val = huffbase[n_count] + offset
625
elif n_count == len(bitamount):
626
#TODO: Implement 64-bit value decompression
634
# Done reading stream, go to the next byte, unless already
635
# at start of new byte
639
# Delta-delta inverse transform
644
newdelta = curdelta*2 - prevdelta + val
647
results.append(newdelta)
650
def dec_gorilla( self, stream, width, num_pts ):
651
'''Decides num_pts values from the Bistream object 'stream',
652
using a fixed width of width'''
653
mask = ( 0xffffffff * pow(2, width-1) ) & 0xffffffff
655
while len(values) < num_pts:
656
value = stream.pop_bits_to_int(width)
659
string = struct.pack('q', value)
660
values.append(struct.unpack('q', string)[0])
664
class Bitstream( object ):
665
'''Takes a character string and turns it into a bitstream'''
667
def __init__( self, data = '\x00'):
672
def pop_byte( self ):
673
'''Removes a byte from the stream, and increments the index'''
674
byte = struct.unpack('<B', self.data[self.index])
679
'''Returns a bit from the stream, incrementing the counters'''
680
mask = pow(2, 7-self.bit)
681
bit = struct.unpack('<B', self.data[self.index])[0] & mask
688
def push_bit( self, bit ):
689
'''Pushes the value of the bit passed to the stream'''
691
val = ((0xff - (pow(2, 8 - self.bit) - 1)) & \
692
struct.unpack('<B', self.data[self.index])[0]) + pow(2, 7 - self.bit)
693
self.data = self.data[0:self.index] + struct.pack('<B', val)
696
def __increment( self ):
697
'''Increments the bit counter, or the index if at last bit of byte'''
703
if self.index == len(self.data):
707
def next_byte( self ):
708
'''Resets the bit counter to 0, and increments the index'''
711
if self.index == len(self.data):
714
def pop_bits_to_int( self, length ):
715
'''Pops length bits from the stream, and returns a little-
716
endian decoded integer value'''
719
while bitcount < length:
721
value += pow(2, length-bitcount-1)
725
def push_int( self, value, length=0 ):
726
'''Pushes the minimum number of bits possible to represent
727
value to the stream, or a specific length if length set'''
730
bitlist.insert(0, value % 2)
732
if length >= len(bitlist):
733
num = length - len(bitlist)
741
def push_count( self, count ):
742
'''Pushes count # 1's to the bitstream, followed by a 0'''
743
for count in range(count):