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
|
||||
|
||||
import os, sys, argparse, os.path, json
|
||||
#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, PngImagePlugin
|
||||
|
||||
formatter = terminal256.Terminal256Formatter()
|
||||
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'
|
||||
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
|
||||
|
||||
import os, sys, argparse, os.path, json
|
||||
import os, sys, os.path
|
||||
from collections import defaultdict
|
||||
#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),
|
||||
|
|
@ -99,27 +99,3 @@ def unpixelterm(text):
|
|||
x, y = 0, y+2
|
||||
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