Made a proper python package out of this.
This commit is contained in:
parent
62e62f1eac
commit
a79ab96953
11 changed files with 176 additions and 173 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
pixelterm.egg-info
|
||||||
|
__pycache__
|
||||||
55
gifterm
Executable file
55
gifterm
Executable file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os, sys, argparse, os.path, json, time, signal, atexit, pixelterm
|
||||||
|
#NOTE: This script uses pygments for RGB->X256 conversion since pygments is
|
||||||
|
#readily available. If you do not like pygments (e.g. because it is large),
|
||||||
|
#you could patch in something like https://github.com/magarcia/python-x256
|
||||||
|
#(but don't forget to send me a pull request ;)
|
||||||
|
from pygments.formatters import terminal256
|
||||||
|
from PIL import Image, GifImagePlugin, ImageSequence
|
||||||
|
|
||||||
|
clear_screen = '\033[H\033[2J'
|
||||||
|
cursor_invisible = '\033[?25l'
|
||||||
|
cursor_visible = '\033[?25h'
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
|
||||||
|
parser.add_argument('image', type=str)
|
||||||
|
parser.add_argument('-s', '--size', type=str, help='Terminal size, [W]x[H]')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
tw, th = os.get_terminal_size()
|
||||||
|
th = th*2
|
||||||
|
if args.size:
|
||||||
|
tw, th = map(int, args.size.split('x'))
|
||||||
|
|
||||||
|
img = Image.open(args.image)
|
||||||
|
palette = img.getpalette()
|
||||||
|
last_frame = Image.new("RGBA", img.size)
|
||||||
|
frames = []
|
||||||
|
|
||||||
|
for frame in ImageSequence.Iterator(img):
|
||||||
|
#This works around a known bug in Pillow
|
||||||
|
#See also: http://stackoverflow.com/questions/4904940/python-converting-gif-frames-to-png
|
||||||
|
frame.putpalette(palette)
|
||||||
|
c = frame.convert("RGBA")
|
||||||
|
|
||||||
|
if img.info['background'] != img.info['transparency']:
|
||||||
|
last_frame.paste(c, c)
|
||||||
|
else:
|
||||||
|
last_frame = c
|
||||||
|
|
||||||
|
im = last_frame.copy()
|
||||||
|
im.thumbnail((tw, th), Image.NEAREST)
|
||||||
|
frames.append(pixelterm.termify_pixels(im))
|
||||||
|
|
||||||
|
print(cursor_invisible)
|
||||||
|
atexit.register(lambda:print(cursor_visible))
|
||||||
|
signal.signal(signal.SIGTERM, lambda signum, stack_frame: exit(1))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for frame in frames:
|
||||||
|
print(clear_screen, pixelterm.reset_sequence)
|
||||||
|
print(frame)
|
||||||
|
time.sleep(img.info['duration']/1000.0)
|
||||||
|
|
||||||
123
gifterm.py
123
gifterm.py
|
|
@ -1,123 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import os, sys, argparse, os.path, json, time, signal, atexit
|
|
||||||
#NOTE: This script uses pygments for RGB->X256 conversion since pygments is
|
|
||||||
#readily available. If you do not like pygments (e.g. because it is large),
|
|
||||||
#you could patch in something like https://github.com/magarcia/python-x256
|
|
||||||
#(but don't forget to send me a pull request ;)
|
|
||||||
from pygments.formatters import terminal256
|
|
||||||
from PIL import Image, GifImagePlugin, ImageSequence
|
|
||||||
|
|
||||||
clear_screen = '\033[H\033[2J'
|
|
||||||
cursor_invisible = '\033[?25l'
|
|
||||||
cursor_visible = '\033[?25h'
|
|
||||||
|
|
||||||
formatter = terminal256.Terminal256Formatter()
|
|
||||||
reset_sequence = terminal256.EscapeSequence(fg=formatter._closest_color(0,0,0), bg=formatter._closest_color(0,0,0)).reset_string()
|
|
||||||
|
|
||||||
def termify_pixels(img):
|
|
||||||
sx, sy = img.size
|
|
||||||
out = ''
|
|
||||||
|
|
||||||
fg,bg = None,None
|
|
||||||
fgd,bgd = {},{}
|
|
||||||
def bgescape(color):
|
|
||||||
nonlocal bg, bgd
|
|
||||||
if bg == color:
|
|
||||||
return ''
|
|
||||||
bg=color
|
|
||||||
if color == (0,0,0,0):
|
|
||||||
return '\033[49m'
|
|
||||||
if color in bgd:
|
|
||||||
return bgd[color]
|
|
||||||
r,g,b,_ = color
|
|
||||||
bgd[color] = '\033[48;5;'+str(formatter._closest_color(r,g,b))+'m'
|
|
||||||
return bgd[color]
|
|
||||||
|
|
||||||
def fgescape(color):
|
|
||||||
nonlocal fg, fgd
|
|
||||||
if fg == color:
|
|
||||||
return ''
|
|
||||||
fg=color
|
|
||||||
r,g,b,_ = color
|
|
||||||
fgd[color] = '\033[38;5;'+str(formatter._closest_color(r,g,b))+'m'
|
|
||||||
return fgd[color]
|
|
||||||
|
|
||||||
def balloon(x,y):
|
|
||||||
if x+1 == img.size[0] or img.im.getpixel((x+1, y)) != (0,255,0,127):
|
|
||||||
w = 1
|
|
||||||
while x-w >= 0 and img.im.getpixel((x-w, y)) == (0,255,0,127):
|
|
||||||
w += 1
|
|
||||||
return '$balloon{}$'.format(w)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
for y in range(0, sy, 2):
|
|
||||||
for x in range(sx):
|
|
||||||
coltop = img.im.getpixel((x, y))
|
|
||||||
colbot = img.im.getpixel((x, y+1)) if y+1 < img.size[1] else (0,0,0,0)
|
|
||||||
|
|
||||||
if coltop[3] == 127: #Control colors
|
|
||||||
out += reset_sequence
|
|
||||||
out += {(255, 0, 0, 127): lambda x,y:'$\\$',
|
|
||||||
(0, 0, 255, 127): lambda x,y:'$/$',
|
|
||||||
(0, 255, 0, 127): balloon
|
|
||||||
}.get(coltop, lambda x,y:' ')(x,y)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if coltop[3] != 255:
|
|
||||||
coltop = (0,0,0,0)
|
|
||||||
if colbot[3] != 255:
|
|
||||||
colbot = (0,0,0,0)
|
|
||||||
|
|
||||||
#Da magicks: ▀█▄
|
|
||||||
c,cf = '▀','█'
|
|
||||||
te,be = fgescape,bgescape
|
|
||||||
if coltop == (0,0,0,0) or ((coltop == bg or colbot == fg) and not colbot == (0,0,0,0)):
|
|
||||||
c,cf,te,be = '▄',' ',be,te
|
|
||||||
if colbot == coltop:
|
|
||||||
c,te,be = cf,te,te
|
|
||||||
out += te(coltop) + be(colbot) + c
|
|
||||||
out = (out.rstrip() if bg == (0,0,0,0) else out) + '\n'
|
|
||||||
return out[:-1] + reset_sequence + '\n'
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
|
|
||||||
parser.add_argument('image', type=str)
|
|
||||||
parser.add_argument('-s', '--size', type=str, help='Terminal size, [W]x[H]')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
tw, th = os.get_terminal_size()
|
|
||||||
th = th*2
|
|
||||||
if args.size:
|
|
||||||
tw, th = map(int, args.size.split('x'))
|
|
||||||
|
|
||||||
img = Image.open(args.image)
|
|
||||||
palette = img.getpalette()
|
|
||||||
last_frame = Image.new("RGBA", img.size)
|
|
||||||
frames = []
|
|
||||||
|
|
||||||
for frame in ImageSequence.Iterator(img):
|
|
||||||
#This works around a known bug in Pillow
|
|
||||||
#See also: http://stackoverflow.com/questions/4904940/python-converting-gif-frames-to-png
|
|
||||||
frame.putpalette(palette)
|
|
||||||
c = frame.convert("RGBA")
|
|
||||||
|
|
||||||
if img.info['background'] != img.info['transparency']:
|
|
||||||
last_frame.paste(c, c)
|
|
||||||
else:
|
|
||||||
last_frame = c
|
|
||||||
|
|
||||||
im = last_frame.copy()
|
|
||||||
im.thumbnail((tw, th), Image.NEAREST)
|
|
||||||
frames.append(termify_pixels(im))
|
|
||||||
|
|
||||||
print(cursor_invisible)
|
|
||||||
atexit.register(lambda:print(cursor_visible))
|
|
||||||
signal.signal(signal.SIGTERM, lambda signum, stack_frame: exit(1))
|
|
||||||
|
|
||||||
while True:
|
|
||||||
for frame in frames:
|
|
||||||
print(clear_screen, reset_sequence)
|
|
||||||
print(frame)
|
|
||||||
time.sleep(img.info['duration']/1000.0)
|
|
||||||
|
|
||||||
28
pixelterm
Executable file
28
pixelterm
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os, sys, argparse, os.path, json, pixelterm
|
||||||
|
from PIL import Image, PngImagePlugin
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
|
||||||
|
parser.add_argument('image', type=str, nargs='*')
|
||||||
|
parser.add_argument('-d', '--output-dir', type=str, help='Output directory (if not given, output to stdout)')
|
||||||
|
args = parser.parse_args()
|
||||||
|
for f in args.image:
|
||||||
|
img = Image.open(f).convert("RGBA")
|
||||||
|
if args.output_dir:
|
||||||
|
print(f)
|
||||||
|
foo, _, _ = f.rpartition('.png')
|
||||||
|
output = os.path.join(args.output_dir, os.path.basename(foo)+'.pony')
|
||||||
|
metadata = json.loads(img.info.get('pixelterm-metadata'))
|
||||||
|
comment = metadata['_comment']
|
||||||
|
del metadata['_comment']
|
||||||
|
metadataarea = '$$$\n' +\
|
||||||
|
'\n'.join([ '\n'.join([ k.upper() + ': ' + v for v in metadata[k] ]) for k in sorted(metadata.keys()) ]) +\
|
||||||
|
'\n' + comment +\
|
||||||
|
'\n$$$\n'
|
||||||
|
with open(output, 'w') as of:
|
||||||
|
of.write(metadataarea)
|
||||||
|
of.write(pixelterm.termify_pixels(img))
|
||||||
|
else:
|
||||||
|
print(pixelterm.termify_pixels(img))
|
||||||
25
pixelterm.py
25
pixelterm.py
|
|
@ -1,12 +1,10 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import os, sys, argparse, os.path, json
|
|
||||||
#NOTE: This script uses pygments for RGB->X256 conversion since pygments is
|
#NOTE: This script uses pygments for RGB->X256 conversion since pygments is
|
||||||
#readily available. If you do not like pygments (e.g. because it is large),
|
#readily available. If you do not like pygments (e.g. because it is large),
|
||||||
#you could patch in something like https://github.com/magarcia/python-x256
|
#you could patch in something like https://github.com/magarcia/python-x256
|
||||||
#(but don't forget to send me a pull request ;)
|
#(but don't forget to send me a pull request ;)
|
||||||
from pygments.formatters import terminal256
|
from pygments.formatters import terminal256
|
||||||
from PIL import Image, PngImagePlugin
|
|
||||||
|
|
||||||
formatter = terminal256.Terminal256Formatter()
|
formatter = terminal256.Terminal256Formatter()
|
||||||
reset_sequence = terminal256.EscapeSequence(fg=formatter._closest_color(0,0,0), bg=formatter._closest_color(0,0,0)).reset_string()
|
reset_sequence = terminal256.EscapeSequence(fg=formatter._closest_color(0,0,0), bg=formatter._closest_color(0,0,0)).reset_string()
|
||||||
|
|
@ -76,26 +74,3 @@ def termify_pixels(img):
|
||||||
out = (out.rstrip() if bg == (0,0,0,0) else out) + '\n'
|
out = (out.rstrip() if bg == (0,0,0,0) else out) + '\n'
|
||||||
return out[:-1] + reset_sequence + '\n'
|
return out[:-1] + reset_sequence + '\n'
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = argparse.ArgumentParser(description='Render pixel images on 256-color ANSI terminals')
|
|
||||||
parser.add_argument('image', type=str, nargs='*')
|
|
||||||
parser.add_argument('-d', '--output-dir', type=str, help='Output directory (if not given, output to stdout)')
|
|
||||||
args = parser.parse_args()
|
|
||||||
for f in args.image:
|
|
||||||
img = Image.open(f).convert("RGBA")
|
|
||||||
if args.output_dir:
|
|
||||||
print(f)
|
|
||||||
foo, _, _ = f.rpartition('.png')
|
|
||||||
output = os.path.join(args.output_dir, os.path.basename(foo)+'.pony')
|
|
||||||
metadata = json.loads(img.info.get('pixelterm-metadata'))
|
|
||||||
comment = metadata['_comment']
|
|
||||||
del metadata['_comment']
|
|
||||||
metadataarea = '$$$\n' +\
|
|
||||||
'\n'.join([ '\n'.join([ k.upper() + ': ' + v for v in metadata[k] ]) for k in sorted(metadata.keys()) ]) +\
|
|
||||||
'\n' + comment +\
|
|
||||||
'\n$$$\n'
|
|
||||||
with open(output, 'w') as of:
|
|
||||||
of.write(metadataarea)
|
|
||||||
of.write(termify_pixels(img))
|
|
||||||
else:
|
|
||||||
print(termify_pixels(img))
|
|
||||||
|
|
|
||||||
50
setup.py
Executable file
50
setup.py
Executable file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
import os, os.path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ver = "1.0"
|
||||||
|
|
||||||
|
def read(filename):
|
||||||
|
return open(os.path.join(os.path.dirname(__file__), filename)).read()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info < (3,0):
|
||||||
|
print('Oops, only python >= 3.0 supported!')
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
setup(name = 'pixelterm',
|
||||||
|
version = ver,
|
||||||
|
description = 'Render pixely images on your terminal. Now also with animated GIF support.',
|
||||||
|
license = 'BSD',
|
||||||
|
author = 'jaseg',
|
||||||
|
author_email = 'pixelterm@jaseg.net',
|
||||||
|
url = 'https://github.com/jaseg/pixelterm',
|
||||||
|
py_modules = ['pixelterm', 'unpixelterm'],
|
||||||
|
scripts = ['pixelterm',
|
||||||
|
'unpixelterm',
|
||||||
|
'colorcube',
|
||||||
|
'gifterm',
|
||||||
|
'resolvecolor',
|
||||||
|
'pngmeta'],
|
||||||
|
zip_safe = True,
|
||||||
|
classifiers = [
|
||||||
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
'Environment :: Console',
|
||||||
|
'Intended Audience :: Information Technology',
|
||||||
|
'Intended Audience :: Intended Audience :: End Users/Desktop',
|
||||||
|
'License :: Freely Distributable',
|
||||||
|
'License :: OSI Approved :: BSD License',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Topic :: Internet',
|
||||||
|
'Topic :: Graphics',
|
||||||
|
'Topic :: System :: Networking'
|
||||||
|
'Topic :: Text Processing :: Filters',
|
||||||
|
'Topic :: Utilities',
|
||||||
|
],
|
||||||
|
|
||||||
|
long_description = read('README.md'),
|
||||||
|
dependency_links = [],
|
||||||
|
)
|
||||||
38
unpixelterm
Executable file
38
unpixelterm
Executable file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os, sys, argparse, os.path, json, unpixelterm
|
||||||
|
#NOTE: This script uses pygments for X256->RGB conversion since pygments is
|
||||||
|
#readily available. If you do not like pygments (e.g. because it is large),
|
||||||
|
#you could patch in something like https://github.com/magarcia/python-x256
|
||||||
|
#(but don't forget to send me a pull request ;)
|
||||||
|
from pygments.formatters import terminal256
|
||||||
|
from PIL import Image, PngImagePlugin
|
||||||
|
try:
|
||||||
|
import re2 as re
|
||||||
|
except:
|
||||||
|
import re
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Convert images rendered by pixelterm-like utilities back to PNG')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true')
|
||||||
|
output_group = parser.add_mutually_exclusive_group()
|
||||||
|
output_group.add_argument('-o', '--output', type=str, help='Output file name, defaults to ${input%.pony}.png')
|
||||||
|
output_group.add_argument('-d', '--output-dir', type=str, help='Place output files here')
|
||||||
|
parser.add_argument('input', type=argparse.FileType('r'), nargs='+')
|
||||||
|
args = parser.parse_args()
|
||||||
|
if len(args.input) > 1 and args.output:
|
||||||
|
parser.print_help()
|
||||||
|
print('You probably do not want to overwrite the given output file {} times.'.format(len(args.input)))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for f in args.input:
|
||||||
|
if len(args.input) > 1:
|
||||||
|
print(f.name)
|
||||||
|
img, metadata = unpixelterm.unpixelterm(f.read())
|
||||||
|
pnginfo = PngImagePlugin.PngInfo()
|
||||||
|
pnginfo.add_text('pixelterm-metadata', json.dumps(metadata))
|
||||||
|
foo, _, _ = f.name.rpartition('.pony')
|
||||||
|
output = args.output or foo+'.png'
|
||||||
|
if args.output_dir:
|
||||||
|
output = os.path.join(args.output_dir, os.path.basename(output))
|
||||||
|
img.save(output, 'PNG', pnginfo=pnginfo)
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import os, sys, argparse, os.path, json
|
import os, sys, os.path
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
#NOTE: This script uses pygments for X256->RGB conversion since pygments is
|
#NOTE: This script uses pygments for X256->RGB conversion since pygments is
|
||||||
#readily available. If you do not like pygments (e.g. because it is large),
|
#readily available. If you do not like pygments (e.g. because it is large),
|
||||||
|
|
@ -99,27 +99,3 @@ def unpixelterm(text):
|
||||||
x, y = 0, y+2
|
x, y = 0, y+2
|
||||||
return img, metadata
|
return img, metadata
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
parser = argparse.ArgumentParser(description='Convert images rendered by pixelterm-like utilities back to PNG')
|
|
||||||
parser.add_argument('-v', '--verbose', action='store_true')
|
|
||||||
output_group = parser.add_mutually_exclusive_group()
|
|
||||||
output_group.add_argument('-o', '--output', type=str, help='Output file name, defaults to ${input%.pony}.png')
|
|
||||||
output_group.add_argument('-d', '--output-dir', type=str, help='Place output files here')
|
|
||||||
parser.add_argument('input', type=argparse.FileType('r'), nargs='+')
|
|
||||||
args = parser.parse_args()
|
|
||||||
if len(args.input) > 1 and args.output:
|
|
||||||
parser.print_help()
|
|
||||||
print('You probably do not want to overwrite the given output file {} times.'.format(len(args.input)))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
for f in args.input:
|
|
||||||
if len(args.input) > 1:
|
|
||||||
print(f.name)
|
|
||||||
img, metadata = unpixelterm(f.read())
|
|
||||||
pnginfo = PngImagePlugin.PngInfo()
|
|
||||||
pnginfo.add_text('pixelterm-metadata', json.dumps(metadata))
|
|
||||||
foo, _, _ = f.name.rpartition('.pony')
|
|
||||||
output = args.output or foo+'.png'
|
|
||||||
if args.output_dir:
|
|
||||||
output = os.path.join(args.output_dir, os.path.basename(output))
|
|
||||||
img.save(output, 'PNG', pnginfo=pnginfo)
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue