Shofel2_T124_python/venv/lib/python3.10/site-packages/asciimatics/renderers/images.py

170 lines
7.1 KiB
Python

# -*- coding: utf-8 -*-
"""
This module implements renderers that produce content based on image files.
"""
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from builtins import object
from builtins import range
from PIL import Image
from asciimatics.renderers.base import StaticRenderer
from asciimatics.screen import Screen
class _ImageSequence(object):
"""
Simple class to make an iterator for a PIL Image object.
"""
def __init__(self, im):
self.im = im
def __getitem__(self, ix):
try:
if ix:
self.im.seek(ix)
return self.im
except EOFError:
raise IndexError
class ImageFile(StaticRenderer):
"""
Renderer to convert an image file (as supported by the Python Imaging
Library) into an ascii grey scale text image.
"""
# The ASCII grey scale from darkest to lightest.
_greyscale = ' .:;rsA23hHG#9&@'
def __init__(self, filename, height=30, colours=8):
"""
:param filename: The name of the file to render.
:param height: The height of the text rendered image.
:param colours: The number of colours the terminal supports.
"""
super(ImageFile, self).__init__()
with Image.open(filename) as image:
background = image.info['background'] if 'background' in \
image.info else None
for frame in _ImageSequence(image):
ascii_image = ""
frame = frame.resize(
(int(frame.size[0] * height * 2.0 / frame.size[1]), height),
Image.BICUBIC)
grey_frame = frame.convert('L')
for py in range(0, grey_frame.size[1]):
ascii_image += "\n"
for px in range(0, grey_frame.size[0]):
real_col = frame.getpixel((px, py))
col = grey_frame.getpixel((px, py))
if real_col == background:
ascii_image += " "
else:
if colours >= 256:
ascii_image += "${%d}" % (232 + col * 23 // 256)
else:
ascii_image += "${%d,%d}" % (
7 if col >= 85 else 0,
Screen.A_BOLD if col < 85 or col > 170 else
Screen.A_NORMAL
)
ascii_image += self._greyscale[
(int(col) * len(self._greyscale)) // 256]
self._images.append(ascii_image)
class ColourImageFile(StaticRenderer):
"""
Renderer to convert an image file (as supported by the Python Imaging
Library) into an block image of available colours.
.. warning::
This is only compatible with 256-colour terminals. Results in other
terminals with reduced colour capabilities are severely restricted.
Since Windows only has 8 base colours, it is recommended that you
avoid this renderer on that platform.
"""
def __init__(self, screen, filename, height=30, bg=Screen.COLOUR_BLACK,
fill_background=False, uni=False, dither=False):
"""
:param screen: The screen to use when displaying the image.
:param filename: The name of the file to render.
:param height: The height of the text rendered image.
:param bg: The default background colour for this image.
:param fill_background: Whether to set background colours too.
:param uni: Whether to use unicode box characters or not.
:param dither: Whether to dither the rendered image or not.
"""
super(ColourImageFile, self).__init__()
with Image.open(filename) as image:
# Find any PNG or GIF background colour.
background = None
if 'background' in image.info:
background = image.info['background']
elif 'transparency' in image.info:
background = image.info['transparency']
# Convert each frame in the image.
for frame in _ImageSequence(image):
ascii_image = ""
frame = frame.resize(
(int(frame.size[0] * height * 2.0 / frame.size[1]),
height * 2 if uni else height),
Image.BICUBIC)
tmp_img = Image.new("P", (1, 1))
tmp_img.putpalette(screen.palette)
# Avoid dithering - this requires a little hack to get directly
# at the underlying library in PIL.
new_frame = frame.convert('RGB')
tmp_img.load()
new_frame.load()
new_frame = new_frame._new(
new_frame.im.convert("P", 3 if dither else 0, tmp_img.im))
# Blank out any transparent sections of the image for complex
# images with alpha blending.
if background is None and frame.mode == 'RGBA':
mask = Image.eval(
frame.split()[-1], lambda a: 255 if a <= 64 else 0)
new_frame.paste(16, mask)
# Decide what "brush" we're going to use for the rendering.
brush = "" if uni else "#"
# Convert the resulting image to coloured ASCII codes.
for py in range(0, new_frame.size[1], 2 if uni else 1):
# Looks like some terminals need a character printed before
# they really reset the colours - so insert a dummy char
# to reset the background if needed.
if uni:
ascii_image += "${%d,2,%d}." % (bg, bg)
ascii_image += "\n"
for px in range(0, new_frame.size[0]):
real_col = frame.getpixel((px, py))
real_col2 = (frame.getpixel((px, py + 1)) if uni else
real_col)
col = new_frame.getpixel((px, py))
col2 = new_frame.getpixel((px, py + 1)) if uni else col
if ((real_col == real_col2 == background) or
(col == col2 == 16)):
if fill_background or uni:
ascii_image += "${%d,2,%d}." % (bg, bg)
else:
ascii_image += "${%d} " % bg
else:
if fill_background or uni:
ascii_image += "${%d,2,%d}%s" % (col2, col,
brush)
else:
ascii_image += "${%d}#" % col
if uni:
ascii_image += "${%d,2,%d}." % (bg, bg)
self._images.append(ascii_image)