diff --git a/examples/convert_img.py b/examples/convert_img.py index 13e3e6d..5f73768 100755 --- a/examples/convert_img.py +++ b/examples/convert_img.py @@ -25,16 +25,15 @@ img_path = Path(args.img_path).as_posix() img = Image.open(img_path) -""" -img: Image.Image = Image.new("RGBA", (4, 4), (255, 0, 0, 255)) +img: Image.Image = Image.new("RGBA", (5, 7), (255, 0, 0, 255)) img.putpixel((0, 0), (255, 0, 0, 0)) -img.putpixel((0, 3), (255, 0, 0, 0)) -img.putpixel((3, 0), (255, 0, 0, 0)) -img.putpixel((3, 3), (255, 0, 0, 0)) +img.putpixel((0, 6), (255, 0, 0, 0)) +img.putpixel((4, 0), (255, 0, 0, 0)) +img.putpixel((4, 6), (255, 0, 0, 0)) img_data_list = list(img.getdata()) -print(np.array(img_data_list).reshape(4,4,4)) -""" +print(np.array(img_data_list).reshape(5,7,4)) + # convert to ansi out = convert_img( img=img, @@ -44,5 +43,5 @@ ) # print to terminal -#print(repr(out)) +print(repr(out)) print(out) diff --git a/src/pil2ansi.py b/src/pil2ansi.py index 4305096..0e06ffa 100644 --- a/src/pil2ansi.py +++ b/src/pil2ansi.py @@ -32,10 +32,10 @@ def pixel_to_color(self, pixel_fg: PIXEL_RGBA, pixel_bg: PIXEL_RGBA) -> str: r, g, b, a = pixel_fg r2, g2, b2, a2 = pixel_bg - format1 = 1 if a == 0 else 2 # 2 is RGB, 1 is transparent - format2 = 1 if a2 == 0 else 2 # 2 is RGB, 1 is transparent + fg_out = f"\033[38;2;{r};{g};{b};" if a != 0 else "\033[38;1;" + bg_out = f"48;2;{r2};{g2};{b2}m" if a2 != 0 else "48;1m" - return f"\033[38;{format1};{r};{g};{b};48;{format2};{r2};{g2};{b2}m" + return f"{fg_out}{bg_out}" @dataclass @@ -47,9 +47,6 @@ def pixel_to_color(self, pixel_fg: PIXEL_LA, pixel_bg: PIXEL_LA) -> str: p1, a1 = pixel_fg p2, a2 = pixel_bg - format1 = 1 if a1 == 0 else 5 # 5 is 8-bit, 1 is transparent - format2 = 1 if a2 == 0 else 5 # 5 is 8-bit, 1 is transparent - num_values = 23 if self.invert == True: @@ -59,7 +56,10 @@ def pixel_to_color(self, pixel_fg: PIXEL_LA, pixel_bg: PIXEL_LA) -> str: val_fg = 232 + int(p1 * num_values / 255) val_bg = 232 + int(p2 * num_values / 255) - return f"\033[38;{format1};{val_fg};48;{format2};{val_bg}m" + fg_out = f"\033[38;5;{val_fg};" if a1 != 0 else "\033[38;1;" + bg_out = f"48;5;{val_bg}m" if a2 != 0 else "48;1m" + + return f"{fg_out}{bg_out}" @dataclass @@ -111,8 +111,8 @@ def convert_img( img = img.resize((new_width, new_height), resample=Image.NEAREST) # crop image to terminal width - if new_width > TERMINAL_WIDTH: - img = img.crop((0, 0, TERMINAL_WIDTH, new_height)) + if img.width > TERMINAL_WIDTH: + img = img.crop((0, 0, TERMINAL_WIDTH, img.height)) pixels = img.getdata() @@ -132,17 +132,16 @@ def convert_img( if i < img.height - 1: pixel_bg = pixels[(i + 1) * img.width + j] else: - pixel_bg = tuple( - [255 for _ in pixel_fg[:-1]] + [0] - ) # makebg transparent on last row + pixel_bg = tuple(pixel_fg[:-1] + (0,)) # makebg transparent on last row if alpha == False: pixel_fg = tuple(pixel_fg[:-1] + (255,)) pixel_bg = tuple(pixel_bg[:-1] + (255,)) + if pixel_fg[-1] == 0 and pixel_bg[-1] == 0: ascii_str += f"{reset_char}{transparent_char}" elif pixel_fg[-1] == 0: - ascii_str += f"{reset_char}{palette.pixel_to_color(pixel_fg=pixel_bg, pixel_bg=pixel_bg)}" + ascii_str += f"{reset_char}{palette.pixel_to_color(pixel_fg=pixel_bg, pixel_bg=pixel_fg)}" if palette != Palettes.ascii: ascii_str += unicode_lower_char else: diff --git a/tests/test_convert_img.py b/tests/test_convert_img.py new file mode 100644 index 0000000..4fade3e --- /dev/null +++ b/tests/test_convert_img.py @@ -0,0 +1,120 @@ +from pil2ansi import convert_img, Palettes +from PIL import Image + +class TestConvertImg: + reset_char: str = "\033[0m" + transparent_char: str = f"{reset_char} " + unicode_upper_char: str = "\u2580" + unicode_lower_char: str = "\u2584" + + RGBA_red_out: str = f"{reset_char}\033[38;2;255;0;0;48;2;255;0;0m{unicode_upper_char}" + RGBA_red_end_row_out: str = f"{reset_char}\033[38;2;255;0;0;48;1m{unicode_upper_char}" + RGBA_red_alpha_upper_out: str = f"{reset_char}\033[38;2;255;0;0;48;1m{unicode_lower_char}" + RGBA_red_alpha_lower_out: str = f"{reset_char}\033[38;2;255;0;0;48;1m{unicode_upper_char}" + + LA_red_out: str = f"{reset_char}\033[38;5;238;48;5;238m{unicode_upper_char}" + LA_red_end_row_out: str = f"{reset_char}\033[38;5;238;48;1m{unicode_upper_char}" + LA_red_alpha_upper_out: str = f"{reset_char}\033[38;5;238;48;1m{unicode_lower_char}" + LA_red_alpha_lower_out: str = f"{reset_char}\033[38;5;238;48;1m{unicode_upper_char}" + + end_row_out: str = f"{reset_char}\n" + + def test_convert_img_color_2x2(self): + img: Image.Image = Image.new("RGBA", (2, 2), (255, 0, 0, 255)) + expected_out: str = self.RGBA_red_out * 2 + self.end_row_out + out = convert_img(img, Palettes.color) + + assert len(img.getdata()) == 4 + assert img.width == 2 + assert out == expected_out + + def test_convert_img_color_4x4(self): + img: Image.Image = Image.new("RGBA", (4, 4), (255, 0, 0, 255)) + expected_out: str = (self.RGBA_red_out * 4 + self.end_row_out) * 2 + out = convert_img(img, Palettes.color) + + assert len(img.getdata()) == 16 + assert img.width == 4 + assert out == expected_out + + def test_convert_img_color_5x7(self): + img: Image.Image = Image.new("RGBA", (5, 7), (255, 0, 0, 255)) + expected_out: str = (self.RGBA_red_out * 5 + self.end_row_out) * 3 + expected_out += self.RGBA_red_end_row_out * 5 + self.end_row_out + out = convert_img(img, Palettes.color) + + assert len(img.getdata()) == 35 + assert img.width == 5 + assert out == expected_out + + def test_convert_img_color_alpha_4x4(self): + img: Image.Image = Image.new("RGBA", (4, 4), (255, 0, 0, 255)) + img.putpixel((0, 0), (255, 0, 0, 0)) + img.putpixel((0, 3), (255, 0, 0, 0)) + img.putpixel((3, 0), (255, 0, 0, 0)) + img.putpixel((3, 3), (255, 0, 0, 0)) + + expected_out: str = ( + self.RGBA_red_alpha_upper_out + + (self.RGBA_red_out * 2) + + self.RGBA_red_alpha_upper_out + + self.end_row_out + ) + expected_out += ( + self.RGBA_red_alpha_lower_out + + (self.RGBA_red_out * 2) + + self.RGBA_red_alpha_lower_out + + self.end_row_out + ) + out = convert_img(img, Palettes.color, alpha=True) + + assert len(img.getdata()) == 16 + assert img.width == 4 + assert out == expected_out + + def test_convert_img_color_alpha_5x7(self): + img: Image.Image = Image.new("RGBA", (5, 7), (255, 0, 0, 255)) + img.putpixel((0, 0), (255, 0, 0, 0)) + img.putpixel((0, 6), (255, 0, 0, 0)) + img.putpixel((4, 0), (255, 0, 0, 0)) + img.putpixel((4, 6), (255, 0, 0, 0)) + + expected_out: str = ( + self.RGBA_red_alpha_upper_out + + (self.RGBA_red_out * 3) + + self.RGBA_red_alpha_upper_out + + self.end_row_out + ) + expected_out += ( + (self.RGBA_red_out * 5) + + self.end_row_out + ) * 2 + expected_out += ( + self.transparent_char + + (self.RGBA_red_alpha_lower_out * 3) + + self.transparent_char + + self.end_row_out + ) + out = convert_img(img, Palettes.color, alpha=True) + print(repr(expected_out)) + print("\n") + print(repr(out)) + print(expected_out) + print("\n") + print(out) + + assert len(img.getdata()) == 35 + assert img.width == 5 + assert out == expected_out + + def test_convert_img_grayscale_2x2(self): + img: Image.Image = Image.new("RGBA", (2, 2), (255, 0, 0, 255)) + expected_out: str = self.LA_red_out * 2 + self.end_row_out + out = convert_img(img, Palettes.grayscale) + + assert len(img.getdata()) == 4 + assert img.width == 2 + assert out == expected_out + + + diff --git a/tests/test_palettes.py b/tests/test_palettes.py index 40462f9..fc1763c 100644 --- a/tests/test_palettes.py +++ b/tests/test_palettes.py @@ -28,16 +28,16 @@ def test_pixel_to_color(self): assert palette.pixel_to_color((0, 255), (127, 255)) == "\033[38;5;232;48;5;243m" assert palette.pixel_to_color((255, 255), (127, 255)) == "\033[38;5;255;48;5;243m" assert palette.pixel_to_color((127, 255), (127, 255)) == "\033[38;5;243;48;5;243m" - assert palette.pixel_to_color((127, 0), (127, 255)) == "\033[38;1;243;48;5;243m" - assert palette.pixel_to_color((0, 255), (127, 0)) == "\033[38;5;232;48;1;243m" + assert palette.pixel_to_color((127, 0), (127, 255)) == "\033[38;1;48;5;243m" + assert palette.pixel_to_color((0, 255), (127, 0)) == "\033[38;5;232;48;1m" def test_pixel_to_char_inverted(self): palette = PaletteGrayscale(invert=True) assert palette.pixel_to_color((0, 255), (127, 255)) == "\033[38;5;255;48;5;244m" assert palette.pixel_to_color((255, 255), (127, 255)) == "\033[38;5;232;48;5;244m" assert palette.pixel_to_color((127, 255), (127, 255)) == "\033[38;5;244;48;5;244m" - assert palette.pixel_to_color((127, 0), (127, 255)) == "\033[38;1;244;48;5;244m" - assert palette.pixel_to_color((0, 255), (127, 0)) == "\033[38;5;255;48;1;244m" + assert palette.pixel_to_color((127, 0), (127, 255)) == "\033[38;1;48;5;244m" + assert palette.pixel_to_color((0, 255), (127, 0)) == "\033[38;5;255;48;1m" class TestPaletteColor: @@ -55,10 +55,10 @@ def test_pixel_to_char(self): assert ( palette.pixel_to_color((255, 127, 255, 0), (255, 255, 0, 255)) - == "\033[38;1;255;127;255;48;2;255;255;0m" + == "\033[38;1;48;2;255;255;0m" ) assert ( palette.pixel_to_color((255, 127, 255, 255), (255, 255, 0, 0)) - == "\033[38;2;255;127;255;48;1;255;255;0m" + == "\033[38;2;255;127;255;48;1m" )