kopia lustrzana https://github.com/EmbroidePy/pyembroidery
Adding Hus
rodzic
5cb7ffe9a7
commit
4aa63ed7b9
|
@ -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]))
|
||||
|
||||
|
|
|
@ -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
|
|
@ -6,6 +6,8 @@ THREAD_MASK = 0x0000FF00
|
|||
NEEDLE_MASK = 0x00FF0000
|
||||
ORDER_MASK = 0xFF000000
|
||||
|
||||
FLAGS_MASK = 0x0000FF00
|
||||
|
||||
NO_COMMAND = -1
|
||||
STITCH = 0
|
||||
JUMP = 1
|
||||
|
@ -65,3 +67,5 @@ CONTINGENCY_SEQUIN_UTILIZE = 0xF5
|
|||
CONTINGENCY_SEQUIN_JUMP = 0xF6
|
||||
CONTINGENCY_SEQUIN_STITCH = 0xF7
|
||||
CONTINGENCY_SEQUIN_REMOVE = 0xF8
|
||||
|
||||
ALTERNATIVE = 0x100 # Generic flag for an alternative form.
|
||||
|
|
|
@ -75,6 +75,7 @@ class Transcoder:
|
|||
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:
|
||||
|
@ -198,6 +199,7 @@ class Transcoder:
|
|||
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
|
||||
|
@ -351,13 +353,17 @@ class Transcoder:
|
|||
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):
|
||||
|
@ -615,7 +621,7 @@ class Transcoder:
|
|||
# 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])
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
|
|
@ -18,6 +18,7 @@ import pyembroidery.FxyReader as FxyReader
|
|||
import pyembroidery.GcodeReader as GcodeReader
|
||||
import pyembroidery.GcodeWriter as GcodeWriter
|
||||
import pyembroidery.GtReader as GtReader
|
||||
import pyembroidery.HusReader as HusReader
|
||||
import pyembroidery.InbReader as InbReader
|
||||
import pyembroidery.JefReader as JefReader
|
||||
import pyembroidery.JefWriter as JefWriter
|
||||
|
@ -462,6 +463,14 @@ def supported_formats():
|
|||
"stitch_z_travel": (5.0, 10.0),
|
||||
},
|
||||
})
|
||||
yield ({
|
||||
"description": "Husqvarna Embroidery Format",
|
||||
"extension": "hus",
|
||||
"extensions": ("hus",),
|
||||
"mimetype": "application/x-hus",
|
||||
"category": "embroidery",
|
||||
"reader": HusReader
|
||||
})
|
||||
|
||||
|
||||
def convert(filename_from, filename_to, settings=None):
|
||||
|
|
|
@ -31,35 +31,35 @@ def read_signed(stream, n):
|
|||
|
||||
def read_sint_8(stream):
|
||||
byte = bytearray(stream.read(1))
|
||||
if len(byte) is 1:
|
||||
if len(byte) == 1:
|
||||
return signed8(byte[0])
|
||||
return None
|
||||
|
||||
|
||||
def read_int_8(stream):
|
||||
byte = bytearray(stream.read(1))
|
||||
if len(byte) is 1:
|
||||
if len(byte) == 1:
|
||||
return byte[0]
|
||||
return None
|
||||
|
||||
|
||||
def read_int_16le(stream):
|
||||
byte = bytearray(stream.read(2))
|
||||
if len(byte) is 2:
|
||||
if len(byte) == 2:
|
||||
return (byte[0] & 0xFF) + ((byte[1] & 0xFF) << 8)
|
||||
return None
|
||||
|
||||
|
||||
def read_int_16be(stream):
|
||||
byte = bytearray(stream.read(2))
|
||||
if len(byte) is 2:
|
||||
if len(byte) == 2:
|
||||
return (byte[1] & 0xFF) + ((byte[0] & 0xFF) << 8)
|
||||
return None
|
||||
|
||||
|
||||
def read_int_24le(stream):
|
||||
b = bytearray(stream.read(3))
|
||||
if len(b) is 3:
|
||||
if len(b) == 3:
|
||||
return (b[0] & 0xFF) + ((b[1] & 0xFF) << 8) + \
|
||||
((b[2] & 0xFF) << 16)
|
||||
return None
|
||||
|
@ -67,7 +67,7 @@ def read_int_24le(stream):
|
|||
|
||||
def read_int_24be(stream):
|
||||
b = bytearray(stream.read(3))
|
||||
if len(b) is 3:
|
||||
if len(b) == 3:
|
||||
return (b[2] & 0xFF) + ((b[1] & 0xFF) << 8) + \
|
||||
((b[0] & 0xFF) << 16)
|
||||
return None
|
||||
|
@ -75,7 +75,7 @@ def read_int_24be(stream):
|
|||
|
||||
def read_int_32le(stream):
|
||||
b = bytearray(stream.read(4))
|
||||
if len(b) is 4:
|
||||
if len(b) == 4:
|
||||
return (b[0] & 0xFF) + ((b[1] & 0xFF) << 8) + \
|
||||
((b[2] & 0xFF) << 16) + ((b[3] & 0xFF) << 24)
|
||||
return None
|
||||
|
@ -83,7 +83,7 @@ def read_int_32le(stream):
|
|||
|
||||
def read_int_32be(stream):
|
||||
b = bytearray(stream.read(4))
|
||||
if len(b) is 4:
|
||||
if len(b) == 4:
|
||||
return (b[3] & 0xFF) + ((b[2] & 0xFF) << 8) + \
|
||||
((b[1] & 0xFF) << 16) + ((b[0] & 0xFF) << 24)
|
||||
return None
|
||||
|
|
|
@ -57,6 +57,7 @@ def read(f, out, settings=None):
|
|||
vp3_read_colorblock(f, out, center_x, center_y)
|
||||
if (i + 1) < count_colors: # Don't add the color change on the final read.
|
||||
out.color_change()
|
||||
out.end()
|
||||
|
||||
|
||||
def vp3_read_colorblock(f, out, center_x, center_y):
|
||||
|
|
|
@ -224,6 +224,7 @@ def write_stitches_block(f, stitches, first_pos_x, first_pos_y):
|
|||
x = stitch[0]
|
||||
y = stitch[1]
|
||||
flags = stitch[2] & COMMAND_MASK
|
||||
alt = stitch[2] & FLAGS_MASK
|
||||
if flags == END:
|
||||
f.write(b'\x80\x03') # Write a trim at end of file.
|
||||
break
|
||||
|
@ -248,7 +249,7 @@ def write_stitches_block(f, stitches, first_pos_x, first_pos_y):
|
|||
last_x += dx
|
||||
last_y += dy
|
||||
if flags == STITCH:
|
||||
if -127 <= dx <= 127 and -127 <= dy <= 127:
|
||||
if -127 <= dx <= 127 and -127 <= dy <= 127 and alt == 0:
|
||||
write_int_8(f, dx)
|
||||
write_int_8(f, dy)
|
||||
else:
|
||||
|
|
Ładowanie…
Reference in New Issue