Skip to content

Commit

Permalink
Merge pull request #76 from EmbroidePy/tatarize-hus
Browse files Browse the repository at this point in the history
Adding Hus
  • Loading branch information
tatarize authored Aug 27, 2019
2 parents 5cb7ffe + f5b5e5a commit ba95f93
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 23 deletions.
8 changes: 4 additions & 4 deletions pyembroidery/DstWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ def encode_record(x, y, flags):
y += 1
if y != 0:
raise ValueError("The dy value given to the writer exceeds maximum allowed.")
elif flags is COLOR_CHANGE:
elif flags == COLOR_CHANGE:
b2 = 0b11000011
elif flags is STOP:
elif flags == STOP:
b2 = 0b11000011
elif flags is END:
elif flags == END:
b2 = 0b11110011
elif flags is SEQUIN_MODE:
elif flags == SEQUIN_MODE:
b2 = 0b01000011
return bytes(bytearray([b0, b1, b2]))

Expand Down
187 changes: 187 additions & 0 deletions pyembroidery/EmbCompress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
def expand(data, uncompressed_size=None):
compress = EmbCompress()
return compress.decompress(data, uncompressed_size)


class Huffman:
def __init__(self, lengths=None, value=0):
self.default_value = value
self.lengths = lengths
self.table = None
self.table_width = 0

def build_table(self):
"""Build an index huffman table based on the lengths. lowest index value wins in a tie."""
self.table_width = max(self.lengths)
self.table = []
size = (1 << self.table_width)
for bit_length in range(1, self.table_width + 1):
size /= 2
for len_index in range(0, len(self.lengths)):
length = self.lengths[len_index]
if length == bit_length:
self.table += [len_index] * size

def lookup(self, byte_lookup):
"""lookup into the index, returns value and length
must be requested with 2 bytes."""
if self.table is None:
return self.default_value, 0
v = self.table[byte_lookup >> (16 - self.table_width)]
return v, self.lengths[v]


class EmbCompress:
def __init__(self):
self.bit_position = 0
self.input_data = None
self.block_elements = None
self.character_huffman = None
self.distance_huffman = None

def get_bits(self, start_pos_in_bits, length):
end_pos_in_bits = start_pos_in_bits + length - 1
start_pos_in_bytes = int(start_pos_in_bits / 8)
end_pos_in_bytes = int(end_pos_in_bits / 8)
value = 0
for i in range(start_pos_in_bytes, end_pos_in_bytes + 1):
value <<= 8
try:
value |= self.input_data[i] & 0xFF
except IndexError:
pass
unused_bits_right_of_sample = (8 - (end_pos_in_bits + 1) % 8) % 8
mask_sample_bits = (1 << length) - 1
original = (value >> unused_bits_right_of_sample) & mask_sample_bits
return original

def pop(self, bit_count):
value = self.peek(bit_count)
self.slide(bit_count)
return value

def peek(self, bit_count):
return self.get_bits(self.bit_position, bit_count)

def slide(self, bit_count):
self.bit_position += bit_count

def read_variable_length(self):
m = self.pop(3)
if m != 7:
return m
for q in range(0, 13): # max read is 16 bit, 3 bits already used. It can't exceed 16-3
s = self.pop(1)
if s == 1:
m += 1
else:
break
return m

def load_character_length_huffman(self):
count = self.pop(5)
if count == 0:
v = self.pop(5)
huffman = Huffman(value=v)
else:
huffman_code_lengths = [0] * count
index = 0
while index < count:
if index == 3: # Special index 3, skip up to 3 elements.
index += self.pop(2)
huffman_code_lengths[index] = self.read_variable_length()
index += 1
huffman = Huffman(huffman_code_lengths, 8)
huffman.build_table()
return huffman

def load_character_huffman(self, length_huffman):
count = self.pop(9)
if count == 0:
v = self.pop(9)
huffman = Huffman(value=v)
else:
huffman_code_lengths = [0] * count
index = 0
while index < count:
h = length_huffman.lookup(self.peek(16))
c = h[0]
self.slide(h[1])
if c == 0: # C == 0, skip 1.
c = 1
index += c
elif c == 1: # C == 1, skip 3 + read(4)
c = 3 + self.pop(4)
index += c
elif c == 2: # C == 2, skip 20 + read(9)
c = 20 + self.pop(9)
index += c
else:
c -= 2
huffman_code_lengths[index] = c
index += 1
huffman = Huffman(huffman_code_lengths)
huffman.build_table()
return huffman

def load_distance_huffman(self):
count = self.pop(5)
if count == 0:
v = self.pop(5)
huffman = Huffman(value=v)
else:
index = 0
lengths = [0] * count
for i in range(0, count):
lengths[index] = self.read_variable_length()
index += 1
huffman = Huffman(lengths)
huffman.build_table()
return huffman

def load_block(self):
self.block_elements = self.pop(16)
character_length_huffman = self.load_character_length_huffman()
self.character_huffman = self.load_character_huffman(character_length_huffman)
self.distance_huffman = self.load_distance_huffman()

def get_token(self):
if self.block_elements <= 0:
self.load_block()
self.block_elements -= 1
h = self.character_huffman.lookup(self.peek(16))
self.slide(h[1])
return h[0]

def get_position(self):
h = self.distance_huffman.lookup(self.peek(16))
self.slide(h[1])
if h[0] == 0:
return 0
v = h[0] - 1
v = (1 << v) + self.pop(v)
return v

def decompress(self, input_data, uncompressed_size=None):
self.input_data = input_data
output_data = []
self.block_elements = -1
bits_total = (len(input_data) * 8)
while bits_total > self.bit_position and (uncompressed_size is None or len(output_data) <= uncompressed_size):
character = self.get_token()
if character <= 255: # literal.
output_data.append(character)
elif character == 510:
break # END
else:
length = character - 253 # Min length is 3. 256-253=3.
back = self.get_position() + 1
position = len(output_data) - back
if back > length:
# Entire lookback is already within output data.
output_data += output_data[position:position + length]
else:
# Will read & write the same data at some point.
for i in range(position, position + length):
output_data.append(output_data[i])
return output_data
4 changes: 4 additions & 0 deletions pyembroidery/EmbConstant.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
NEEDLE_MASK = 0x00FF0000
ORDER_MASK = 0xFF000000

FLAGS_MASK = 0x0000FF00

NO_COMMAND = -1
STITCH = 0
JUMP = 1
Expand Down Expand Up @@ -65,3 +67,5 @@
CONTINGENCY_SEQUIN_JUMP = 0xF6
CONTINGENCY_SEQUIN_STITCH = 0xF7
CONTINGENCY_SEQUIN_REMOVE = 0xF8

ALTERNATIVE = 0x100 # Generic flag for an alternative form.
10 changes: 8 additions & 2 deletions pyembroidery/EmbEncoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def __init__(self, settings=None):
self.state_jumping = False
self.needle_x = 0
self.needle_y = 0
self.high_flags = 0

def transcode(self, source_pattern, destination_pattern):
if source_pattern is destination_pattern:
Expand Down Expand Up @@ -198,6 +199,7 @@ def transcode_main(self):
x = round(x)
y = round(y)
flags = self.stitch[2] & COMMAND_MASK
self.high_flags = self.stitch[2] & FLAGS_MASK

if flags == NO_COMMAND:
continue
Expand Down Expand Up @@ -351,13 +353,17 @@ def declare_not_trimmed(self):
self.state_trimmed = False

def add_thread_change(self, command, thread=None, needle=None, order=None):
self.add(encode_thread_change(command, thread, needle, order))
x = self.needle_x
y = self.needle_y
cmd = encode_thread_change(command, thread, needle, order)
self.destination_pattern.stitches.append([x, y, cmd])

def add(self, flags, x=None, y=None):
if x is None:
x = self.needle_x
if y is None:
y = self.needle_y
flags |= self.high_flags
self.destination_pattern.stitches.append([x, y, flags])

def lookahead_stitch(self):
Expand Down Expand Up @@ -615,7 +621,7 @@ def interpolate_gap_stitches(self, x0, y0, x1, y1, max_length, data):
# we need the gap stitches only, not start or end stitch.
qx += step_size_x
qy += step_size_y
stitch = [qx, qy, data]
stitch = [qx, qy, data | self.high_flags]
transcode.append(stitch)
self.update_needle_position(stitch[0], stitch[1])

Expand Down
2 changes: 1 addition & 1 deletion pyembroidery/EmbThread.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def __str__(self):
if self.description is None:
return "EmbThread %s" % self.hex_color()
else:
return "EmbThread %s %s" % self.description, self.hex_color()
return "EmbThread %s %s" % (self.description, self.hex_color())

def set_color(self, r, g, b):
self.color = color_rgb(r, g, b)
Expand Down
46 changes: 46 additions & 0 deletions pyembroidery/EmbThreadHus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from .EmbThread import EmbThread


def get_thread_set():
return [
EmbThreadHus("#000000", "Black", "026"),
EmbThreadHus("#0000e7", "Blue", "005"),
EmbThreadHus("#00c600", "Green", "002"),
EmbThreadHus("#ff0000", "Red", "014"),
EmbThreadHus("#840084", "Purple", "008"),
EmbThreadHus("#ffff00", "Yellow", "020"),
EmbThreadHus("#848484", "Grey", "024"),
EmbThreadHus("#8484e7", "Light Blue", "006"),
EmbThreadHus("#00ff84", "Light Green", "003"),
EmbThreadHus("#ff7b31", "Orange", "017"),
EmbThreadHus("#ff8ca5", "Pink", "011"),
EmbThreadHus("#845200", "Brown", "028"),
EmbThreadHus("#ffffff", "White", "022"),
EmbThreadHus("#000084", "Dark Blue", "004"),
EmbThreadHus("#008400", "Dark Green", "001"),
EmbThreadHus("#7b0000", "Dark Red", "013"),
EmbThreadHus("#ff6384", "Light Red", "015"),
EmbThreadHus("#522952", "Dark Purple", "007"),
EmbThreadHus("#ff00ff", "Light Purple", "009"),
EmbThreadHus("#ffde00", "Dark Yellow", "019"),
EmbThreadHus("#ffff9c", "Light Yellow", "021"),
EmbThreadHus("#525252", "Dark Grey", "025"),
EmbThreadHus("#d6d6d6", "Light Grey", "023"),
EmbThreadHus("#ff5208", "Dark Orange", "016"),
EmbThreadHus("#ff9c5a", "Light Orange", "018"),
EmbThreadHus("#ff52b5", "Dark Pink", "010"),
EmbThreadHus("#ffc6de", "Light Pink", "012"),
EmbThreadHus("#523100", "Dark Brown", "027"),
EmbThreadHus("#b5a584", "Light Brown", "029")
]


class EmbThreadHus(EmbThread):
def __init__(self, color, description, catalog_number=None):
EmbThread.__init__(self)
self.set(color)
self.description = description
self.catalog_number = catalog_number
self.brand = "Hus"
self.chart = "Hus"

2 changes: 1 addition & 1 deletion pyembroidery/ExpWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def write(pattern, f, settings=None):
dy = int(round(y - yy))
xx += dx
yy += dy
if data is STITCH:
if data == STITCH:
# consider bounds checking the delta_x, delta_y and raising ValueError if exceeds.
delta_x = dx & 0xFF
delta_y = -dy & 0xFF
Expand Down
59 changes: 59 additions & 0 deletions pyembroidery/HusReader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from .EmbCompress import expand
from .EmbThreadHus import get_thread_set
from .ReadHelper import signed8, signed16, read_int_32le, read_int_16le, read_string_8


def read(f, out, settings=None):
magic_code = read_int_32le(f)
number_of_stitches = read_int_32le(f)
number_of_colors = read_int_32le(f)

extend_pos_x = signed16(read_int_16le(f))
extend_pos_y = signed16(read_int_16le(f))
extend_neg_x = signed16(read_int_16le(f))
extend_neg_y = signed16(read_int_16le(f))

command_offset = read_int_32le(f)
x_offset = read_int_32le(f)
y_offset = read_int_32le(f)

string_value = read_string_8(f, 8)

unknown_16_bit = read_int_16le(f)

hus_thread_set = get_thread_set()
for i in range(0, number_of_colors):
index = read_int_16le(f)
out.add_thread(hus_thread_set[index])
f.seek(command_offset, 0)
command_compressed = bytearray(f.read(x_offset - command_offset))
f.seek(x_offset, 0)
x_compressed = bytearray(f.read(y_offset - x_offset))
f.seek(y_offset, 0)
y_compressed = bytearray(f.read())

command_decompressed = expand(command_compressed, number_of_stitches)
x_decompressed = expand(x_compressed, number_of_stitches)
y_decompressed = expand(y_compressed, number_of_stitches)

stitch_count = min(len(command_decompressed), len(x_decompressed), len(y_decompressed))

for i in range(0, stitch_count):
cmd = command_decompressed[i]
x = signed8(x_decompressed[i])
y = -signed8(y_decompressed[i])
if cmd == 0x80: # STITCH
out.stitch(x, y)
elif cmd == 0x81: # JUMP
out.move(x, y)
elif cmd == 0x84: # COLOR_CHANGE
out.color_change(x, y)
elif cmd == 0x88: # TRIM
if x != 0 or y != 0:
out.move(x, y)
out.trim()
elif cmd == 0x90: # END
break
else: # UNMAPPED COMMAND
break
out.end()
2 changes: 1 addition & 1 deletion pyembroidery/PecWriter.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def pec_encode(pattern, f):
dy = int(round(y - yy))
xx += dx
yy += dy
if data is STITCH:
if data == STITCH:
if jumping and dx != 0 and dy != 0:
f.write(b'\x00\x00')
jumping = False
Expand Down
Loading

0 comments on commit ba95f93

Please sign in to comment.