pyembroidery/pyembroidery/Vp3Writer.py

263 wiersze
8.5 KiB
Python

from .EmbConstant import *
from .WriteHelper import write_int_8, write_int_24be, \
write_int_32be, write_int_16be, write_string_utf8, write_string
SEQUIN_CONTINGENCY = CONTINGENCY_SEQUIN_JUMP
FULL_JUMP = False
# Since jump is stitch, full jump will result in pointless double stitch.
# Vp3 can encode signed 16 bit deltas.
MAX_JUMP_DISTANCE = 3200
# coded beyond 255 tho, they count as jumps.
MAX_STITCH_DISTANCE = 255
def vp3_write_string_8(stream, string):
write_int_16be(stream, len(string))
write_string_utf8(stream, string)
# vp3_write_length_and_bytes(stream, bytestring)
def vp3_write_string_16(stream, string):
write_int_16be(stream, len(string) * 2)
write_string(stream, string, "UTF-16BE")
# vp3_write_length_and_bytes(stream, bytestring)
def vp3_write_length_and_bytes(stream, bytestring):
write_int_16be(stream, len(bytestring))
stream.write(bytestring)
def vp3_patch_byte_offset(stream, offset):
currentPos = stream.tell()
stream.seek(offset, 0) # Absolute position seek.
position = currentPos - offset - 4 # 4 bytes int32
write_int_32be(stream, position)
stream.seek(currentPos, 0) # Absolute position seek.
def get_as_colorblocks(pattern):
thread_index = 0
last_pos = 0
end = len(pattern.stitches)
for pos, stitch in enumerate(pattern.stitches):
if stitch[2] != COLOR_CHANGE:
continue
thread = pattern.get_thread_or_filler(thread_index)
thread_index += 1
yield (pattern.stitches[last_pos:pos], thread)
last_pos = pos
thread = pattern.get_thread_or_filler(thread_index)
thread_index += 1
yield (pattern.stitches[last_pos:end], thread)
def write(pattern, f, settings=None):
pattern.fix_color_count()
write_string_utf8(f, "%vsm%")
write_int_8(f, 0)
vp3_write_string_16(f, "Produced by Software Ltd")
write_file(pattern, f)
def write_file(pattern, f):
f.write(b'\x00\x02\x00')
placeholder_distance_end_of_file_block_020 = f.tell()
write_int_32be(f, 0) # placeholder
# This refers to the end of the final block, not entire bytes.
vp3_write_string_16(f, "")
# This is global notes and settings string.
# "Setting:" followed by settings text.
count_stitches = len(pattern.stitches)
colorblocks = [i for i in get_as_colorblocks(pattern)]
count_colorblocks_total = len(colorblocks)
extends = pattern.extends()
write_int_32be(f, int(extends[2] * 100)) # right
write_int_32be(f, int(extends[1] * -100)) # -top
write_int_32be(f, int(extends[0] * 100)) # left
write_int_32be(f, int(extends[3] * -100)) # -bottom
# EmbroiderModder Comment:
# "this would be some(unknown) function of thread length"
# Wilcom: 0C 54 == 3156
# Note, this is the total stitch count, sans end command.
ends = pattern.count_stitch_commands(END)
count_just_stitches = count_stitches - ends
write_int_32be(f, count_just_stitches)
write_int_8(f, 0)
write_int_8(f, count_colorblocks_total)
write_int_8(f, 12) # 0xC
write_int_8(f, 0)
count_designs = 1
write_int_8(f, count_designs) # Number of designs.
for i in range(0, count_designs):
write_design_block(f, extends, colorblocks)
vp3_patch_byte_offset(f, placeholder_distance_end_of_file_block_020)
def write_design_block(f, extends, colorblocks):
f.write(b'\x00\x03\x00')
placeholder_distance_end_of_design_block_030 = f.tell()
write_int_32be(f, 0)
count_colorblocks_total = len(colorblocks)
width = extends[2] - extends[0]
height = extends[3] - extends[1]
half_width = width / 2
half_height = height / 2
center_x = extends[2] - half_width
center_y = extends[3] - half_height
write_int_32be(f, int(center_x) * 100) # initial x
write_int_32be(f, int(center_y) * -100) # initial y
write_int_8(f, 0)
write_int_8(f, 0)
write_int_8(f, 0)
# extends 2
write_int_32be(f, int(half_width) * -100)
write_int_32be(f, int(half_width) * 100)
write_int_32be(f, int(half_height) * -100)
write_int_32be(f, int(half_height) * 100)
write_int_32be(f, int(width) * 100)
write_int_32be(f, int(height) * 100)
vp3_write_string_16(f, "") # This is notes and settings string.
f.write(b'\x64\x64') # write_int_16be(f, 25700)
# maybe b'dd', maybe 100, 100
write_int_32be(f, 4096) # b'\x00\x00\x10\x00'
write_int_32be(f, 0) # b'\x00\x00\x00\x00'
write_int_32be(f, 0) # b'\x00\x00\x10\x00'
write_int_32be(f, 4096) # b'\x00\x00\x10\x00'
f.write(b'xxPP\x01\x00')
vp3_write_string_16(f, "Produced by Software Ltd")
write_int_16be(f, count_colorblocks_total)
first = True
for colorblock in colorblocks:
stitches = colorblock[0]
thread = colorblock[1]
write_vp3_colorblock(f, first, center_x, center_y, stitches, thread)
first = False
vp3_patch_byte_offset(f, placeholder_distance_end_of_design_block_030)
def write_vp3_colorblock(f, first, center_x, center_y, stitches, thread):
f.write(b'\x00\x05\x00')
placeholder_distance_end_of_color_block_050 = f.tell()
write_int_32be(f, 0)
if len(stitches) > 0:
first_pos_x = stitches[0][0]
first_pos_y = stitches[0][1]
if first:
first_pos_x = 0
first_pos_y = 0
last_pos_x = stitches[-1][0]
last_pos_y = stitches[-1][1]
else:
first_pos_x = 0
first_pos_y = 0
last_pos_x = 0
last_pos_y = 0
start_position_from_center_x = first_pos_x - center_x
start_position_from_center_y = -(first_pos_y - center_y)
write_int_32be(f, int(start_position_from_center_x) * 100)
write_int_32be(f, int(start_position_from_center_y) * 100)
vp3_write_thread(f, thread)
block_shift_x = last_pos_x - first_pos_x
block_shift_y = -(last_pos_y - first_pos_y)
write_int_32be(f, int(block_shift_x) * 100)
write_int_32be(f, int(block_shift_y) * 100)
write_stitches_block(f, stitches, first_pos_x, first_pos_y)
write_int_8(f, 0)
vp3_patch_byte_offset(f, placeholder_distance_end_of_color_block_050)
def vp3_write_thread(f, thread):
f.write(b'\x01\x00') # Single color, no transition.
write_int_24be(f, thread.color)
f.write(b'\x00\x00\x00\x05\x28') # no parts, no length, Rayon 40-weight
if thread.catalog_number is not None:
vp3_write_string_8(f, thread.catalog_number)
else:
vp3_write_string_8(f, "")
if thread.description is not None:
vp3_write_string_8(f, thread.description)
else:
vp3_write_string_8(f, thread.hex_color())
if thread.brand is not None:
vp3_write_string_8(f, thread.brand)
else:
vp3_write_string_8(f, "")
def write_stitches_block(f, stitches, first_pos_x, first_pos_y):
# Embroidermodder code has
# vp3_write_string(f, "\x00");
# The 0, x, 0 bytes come before placeholders
# Given this consisistency, it's doubtful this is a string.
# Those aren't
f.write(b'\x00\x01\x00')
placeholder_distance_to_end_of_stitches_block_010 = f.tell()
write_int_32be(f, 0) # placeholder
f.write(b'\x0A\xF6\x00')
last_x = first_pos_x
last_y = first_pos_y
for stitch in stitches:
x = stitch[0]
y = stitch[1]
flags = stitch[2]
if flags == END:
f.write(b'\x80\x03')
break
elif flags == COLOR_CHANGE:
continue
elif flags == TRIM:
continue
elif flags == SEQUIN_MODE:
continue
elif flags == SEQUIN_EJECT:
continue
elif flags == STOP:
# Not sure what to do here.
# f.write(b'\x80\x04')
continue
elif flags == JUMP:
# Since VP3.Jump == VP3.Stitch, we combine jumps.
continue
dx = int(x - last_x)
dy = int(y - last_y)
last_x += dx
last_y += dy
if flags == STITCH:
if -127 <= dx <= 127 and -127 <= dy <= 127:
write_int_8(f, dx)
write_int_8(f, dy)
else:
f.write(b'\x80\x01')
write_int_16be(f, dx)
write_int_16be(f, dy)
f.write(b'\x80\x02')
# VSM gave ending stitches as 80 03 35 A5, so, 80 03 isn't strictly end.
vp3_patch_byte_offset(f, placeholder_distance_to_end_of_stitches_block_010)