Porównaj commity

...

2 Commity

Autor SHA1 Wiadomość Data
Tatarize db3874907a Add in tests for write-outs 2023-07-03 13:21:36 -07:00
Tatarize 7ed9a1270b Improve fancy-output 2023-07-03 13:21:19 -07:00
2 zmienionych plików z 192 dodań i 61 usunięć

Wyświetl plik

@ -1,5 +1,6 @@
import struct
import zlib
from math import sqrt
from .EmbConstant import *
from .EmbThread import EmbThread
@ -7,6 +8,7 @@ from .EmbThread import EmbThread
SEQUIN_CONTINGENCY = CONTINGENCY_SEQUIN_STITCH
FULL_JUMP = True
# Static characters for writing to image.
characters = {
"0": [
[9, 9, 4, 1, 0, 1, 5, 9, 9],
@ -192,6 +194,9 @@ characters = {
def write_png(buf, width, height):
"""
Writes PNG file to disk. Buffer must be RGBA * width * height
"""
width_byte_4 = width * 4
raw_data = b"".join(
b"\x00" + buf[span : span + width_byte_4]
@ -220,29 +225,91 @@ class PngBuffer:
def __init__(self, width, height):
self.width = int(width + 3)
self.height = int(height + 3)
self.buf = [0] * (4 * self.width * self.height)
self.red = 0
self.green = 0
self.blue = 0
self.alpha = 0
self.buf = bytearray(4 * self.width * self.height)
self.line_width = 3
self.fancy = True
self.fancy_stops = (0, 0.05, 0.5, 0.95, 0)
self.values = (-120, 0, -120, 0, -120)
self._red = 0
self._green = 0
self._blue = 0
self._alpha = 0
self._distance_from_black = 0
self._gradient_shade_ends = 0.65
self._gradient_shade_edge = 1.1
self._gradient_shade_center = 1.55
self._gradient_color_position1 = 0.40
self._gradient_color_position2 = 0.50
self._gradient_color_position3 = 0.70
def set_color(self, r, g, b):
self.red = r
self.green = g
self.blue = b
def modify_gradient(
self,
gradient_shade_ends=0.65,
gradient_shade_edge=1.1,
gradient_shade_center=1.55,
gradient_color_position1=0.40,
gradient_color_position2=0.50,
gradient_color_position3=0.70,
):
self._gradient_shade_ends = gradient_shade_ends
self._gradient_shade_edge = gradient_shade_edge
self._gradient_shade_center = gradient_shade_center
self._gradient_color_position1 = gradient_color_position1
self._gradient_color_position2 = gradient_color_position2
self._gradient_color_position3 = gradient_color_position3
def gradient(self, v):
if v <= 0.05:
return ((v - 0) * (1/0.05)) * (1.0 - 0.5) + 0.5
if v <= 0.5:
return ((v - 0.05) * (1/0.45)) * (0.5 - 1.0) + 1.0
if v <= 0.9:
return ((v - 0.5) * (1 / 0.40)) * (1.0 - 0.5) + 0.5
return ((v - .9) * (1 / 0.1)) * (0.5 - 1.0) + 1.0
def set_color(self, r, g, b, a=255):
self._red = r
self._green = g
self._blue = b
self._alpha = a
rmean = int(r / 2)
self._distance_from_black = sqrt(
(((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8)
)
def gradient(self, position_in_line):
"""
This function gives different value scales between 0 and 1 which are used to multiply
all the components of the color to create a darker value. This generally works save for
black which is already (0,0,0) and can't increase value.
"""
if (
position_in_line <= self._gradient_color_position1
): # start_of_grdient -> Position 1
from_shade = self._gradient_shade_ends
to_shade = self._gradient_shade_edge
range = self._gradient_color_position1 - 0 # Range of transition
amount = (position_in_line - 0) * (1 / range)
# light to extra_light
elif (
position_in_line <= self._gradient_color_position2
): # Position 1 -> Position 2.
from_shade = self._gradient_shade_edge
to_shade = self._gradient_shade_center
range = self._gradient_color_position2 - self._gradient_color_position1
amount = (position_in_line - self._gradient_color_position1) * (1 / range)
# center of gradient
# extra_light to light
elif (
position_in_line <= self._gradient_color_position3
): # Position 2 -> Position 3
from_shade = self._gradient_shade_center
to_shade = self._gradient_shade_edge
range = self._gradient_color_position3 - self._gradient_color_position2
amount = (position_in_line - self._gradient_color_position2) * (1 / range)
# light to dark
elif position_in_line <= 1: # Position 3 -> end_of_gradient
from_shade = self._gradient_shade_edge
to_shade = self._gradient_shade_ends
range = 1 - self._gradient_color_position3
amount = (position_in_line - self._gradient_color_position3) * (1 / range)
else:
raise ValueError("Did not occur within line.")
v = amount * (to_shade - from_shade) + from_shade
return max(min(v, 1.0), 0.0)
def background(self, red, green, blue, alpha):
for i in range(0, len(self.buf), 4):
@ -251,23 +318,69 @@ class PngBuffer:
self.buf[i + 2] = blue
self.buf[i + 3] = alpha
def plot(self, x, y, v=None):
def plot(self, x, y, v=None, a=None):
"""
Plot a particular point in the canvas.
:param x: x position to plot
:param y: y position to plot
:param v: value scale of particular color (0-1)
:param a: alpha to set for this particular plot if not self.alpha
:return:
"""
a = int(a) if a is not None else self._alpha
if v is None:
v = 1.0
try:
x += 1
y += 1
pos = (self.width * y) + x
idx = pos * 4
r = self.red
g = self.green
b = self.blue
if v is not None:
r = int(r * v)
g = int(g * v)
b = int(b * v)
self.buf[idx] = r
self.buf[idx + 1] = g
self.buf[idx + 2] = b
self.buf[idx + 3] = self.alpha
background_a = self.buf[idx + 3]
# get rgb and check if close to black and make off dark gray
# this makes black have highlights
if self._distance_from_black < 15:
r = 35
g = 35
b = 35
r = r * v
g = g * v
b = b * v
# end of check and make gray
else:
r = self._red * v
g = self._green * v
b = self._blue * v
if 0 > r:
r = 0
if 0 > g:
g = 0
if 0 > b:
b = 0
if r > 255:
r = 255
if g > 255:
g = 255
if b > 255:
b = 255
if background_a != 0 and a != 255:
s_alpha = a / 255.0
s_background_a = background_a / 255.0
one_minus_salpha = 1 - s_alpha
background_r = self.buf[idx]
background_g = self.buf[idx + 1]
background_b = self.buf[idx + 2]
r = r * s_alpha + one_minus_salpha * background_r
g = g * s_alpha + one_minus_salpha * background_g
b = b * s_alpha + one_minus_salpha * background_b
a = (s_alpha + one_minus_salpha * s_background_a) * 255
self.buf[idx] = int(r)
self.buf[idx + 1] = int(g)
self.buf[idx + 2] = int(b)
self.buf[idx + 3] = (
int(a) - 4
) # remove just a little opacity for background color tint show thru
except IndexError:
pass
@ -320,8 +433,10 @@ class PngBuffer:
w = self.line_width
left = w >> 1
right = w - left
v = self.gradient(index/max_pos)
if self.fancy:
v = self.gradient(index / max_pos)
else:
v = 1.0
if dy:
for pos in range(-left, right):
self.plot(x + pos, y, v)
@ -364,6 +479,27 @@ class PngBuffer:
x += 11
def draw_guides(draw_buff, extends):
width = int(extends[2] - extends[0])
height = int(extends[3] - extends[1])
draw_buff.set_color(0, 0, 0, 255)
draw_buff.line_width = 1
min_x = int(extends[0])
min_y = int(extends[1])
points = 50
draw_buff.draw_text(0, 0, "mm")
for x in range(points - (min_x % points), width - 30, points):
if x < 30:
continue
draw_buff.draw_text(x, 0, str(int((x + min_x) / 10)), rotate=True)
draw_buff.draw_line(x, 0, x, 30)
for y in range(points - (min_y % points), height - 30, points):
if y < 30:
continue
draw_buff.draw_text(0, y, str(int((y + min_y) / 10)))
draw_buff.draw_line(0, y, 30, y)
def write(pattern, f, settings=None):
guides = settings.get("guides", False)
extends = pattern.bounds()
@ -371,23 +507,23 @@ def write(pattern, f, settings=None):
width = int(extends[2] - extends[0])
height = int(extends[3] - extends[1])
draw_buff = PngBuffer(width, height)
draw_buff.fancy = settings.get("fancy", False)
if settings is not None:
background = settings.get("background", None)
linewidth = settings.get("linewidth", None)
background = settings.get("background")
if background is not None:
b = EmbThread()
b.set(background)
draw_buff.background(b.get_red(), b.get_green(), b.get_blue(), 0xFF)
linewidth = settings.get("linewidth")
if linewidth is not None and isinstance(linewidth, int):
draw_buff.line_width = linewidth
for stitchblock in pattern.get_as_stitchblock():
block = stitchblock[0]
thread = stitchblock[1]
draw_buff.red = thread.get_red()
draw_buff.green = thread.get_green()
draw_buff.blue = thread.get_blue()
draw_buff.alpha = 255
draw_buff.set_color(
thread.get_red(), thread.get_green(), thread.get_blue(), 255
)
last_x = None
last_y = None
for stitch in block:
@ -399,26 +535,6 @@ def write(pattern, f, settings=None):
last_y = y
if guides:
draw_buff.red = 0
draw_buff.green = 0
draw_buff.blue = 0
draw_buff.alpha = 255
draw_buff.line_width = 1
min_x = extends[0]
min_y = extends[1]
points = 50
draw_buff.draw_text(0, 0, "mm")
for x in range(points - (min_x % points), width - 30, points):
if x < 30:
continue
draw_buff.draw_text(x, 0, str(int((x + min_x) / 10)), rotate=True)
draw_buff.draw_line(x, 0, x, 30)
for y in range(points - (min_y % points), height - 30, points):
if y < 30:
continue
draw_buff.draw_text(0, y, str(int((y + min_y) / 10)))
draw_buff.draw_line(0, y, 30, y)
draw_guides(draw_buff, extends)
f.write(
write_png(bytes(bytearray(draw_buff.buf)), draw_buff.width, draw_buff.height)
)
f.write(write_png(draw_buff.buf, draw_buff.width, draw_buff.height))

Wyświetl plik

@ -16,6 +16,21 @@ class TestWrites(unittest.TestCase):
write_png(get_shift_pattern(), file1, {"background": "#F00", "linewidth": 5})
self.addCleanup(os.remove, file1)
def test_write_fancy_png(self):
file1 = "file-fancy.png"
write_png(get_shift_pattern(), file1, {"background": "#F00", "linewidth": 5, "fancy": True})
self.addCleanup(os.remove, file1)
def test_write_guides_png(self):
file1 = "file-guides.png"
write_png(get_shift_pattern(), file1, {"background": "#F00", "linewidth": 5, "guides": True})
self.addCleanup(os.remove, file1)
def test_write_fancy_guides_png(self):
file1 = "file-fancy-guides.png"
write_png(get_shift_pattern(), file1, {"background": "#F00", "linewidth": 5, "fancy": True, "guides": True})
self.addCleanup(os.remove, file1)
def test_write_dst_read_dst(self):
file1 = "file.dst"
write_dst(get_big_pattern(), file1)