From a79ab969533320582482856758394e74739b152e Mon Sep 17 00:00:00 2001 From: jaseg Date: Mon, 29 Jul 2013 18:51:18 +0200 Subject: Made a proper python package out of this. --- .gitignore | 4 ++ colorcube | 23 +++++++++++ colorcube.py | 23 ----------- gifterm | 55 +++++++++++++++++++++++++ gifterm.py | 123 -------------------------------------------------------- pixelterm | 28 +++++++++++++ pixelterm.py | 25 ------------ pngmeta | 11 +++++ pngmeta.py | 11 ----- resolvecolor | 15 +++++++ resolvecolor.py | 15 ------- setup.py | 50 +++++++++++++++++++++++ unpixelterm | 38 +++++++++++++++++ unpixelterm.py | 26 +----------- 14 files changed, 225 insertions(+), 222 deletions(-) create mode 100644 .gitignore create mode 100755 colorcube delete mode 100755 colorcube.py create mode 100755 gifterm delete mode 100755 gifterm.py create mode 100755 pixelterm create mode 100755 pngmeta delete mode 100755 pngmeta.py create mode 100755 resolvecolor delete mode 100755 resolvecolor.py create mode 100755 setup.py create mode 100755 unpixelterm diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d7bfbb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build +dist +pixelterm.egg-info +__pycache__ diff --git a/colorcube b/colorcube new file mode 100755 index 0000000..ebf36b9 --- /dev/null +++ b/colorcube @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +import os, sys, argparse, os.path, json + +# Display an xterm-256color color palette on the terminal, including color ids + +reset_sequence = '\033[39;49m' + +def esc(i): + return '\033[48;5;'+str(i)+'m' + +print(''.join([str(i).ljust(4) for i in range(16)])) +print(' '.join([esc(i) for i in range(16)])+' ' + reset_sequence) + +for j in range(6): + for k in range(6): + c = 16+j*6+k*6*6 + print(''.join([str(c+i).ljust(4) for i in range(6)])) + print(' '.join([esc(c+i) for i in range(6)])+' ' + reset_sequence) + +print(''.join([str(i).ljust(4) for i in range(16+6*6*6, 16+6*6*6+24)])) +print(' '.join([esc(i) for i in range(16+6*6*6, 16+6*6*6+24)])+' ' + reset_sequence) + diff --git a/colorcube.py b/colorcube.py deleted file mode 100755 index ebf36b9..0000000 --- a/colorcube.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -import os, sys, argparse, os.path, json - -# Display an xterm-256color color palette on the terminal, including color ids - -reset_sequence = '\033[39;49m' - -def esc(i): - return '\033[48;5;'+str(i)+'m' - -print(''.join([str(i).ljust(4) for i in range(16)])) -print(' '.join([esc(i) for i in range(16)])+' ' + reset_sequence) - -for j in range(6): - for k in range(6): - c = 16+j*6+k*6*6 - print(''.join([str(c+i).ljust(4) for i in range(6)])) - print(' '.join([esc(c+i) for i in range(6)])+' ' + reset_sequence) - -print(''.join([str(i).ljust(4) for i in range(16+6*6*6, 16+6*6*6+24)])) -print(' '.join([esc(i) for i in range(16+6*6*6, 16+6*6*6+24)])+' ' + reset_sequence) - diff --git a/gifterm b/gifterm new file mode 100755 index 0000000..99cbb5f --- /dev/null +++ b/gifterm @@ -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) + diff --git a/gifterm.py b/gifterm.py deleted file mode 100755 index cef85b9..0000000 --- a/gifterm.py +++ /dev/null @@ -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) - diff --git a/pixelterm b/pixelterm new file mode 100755 index 0000000..f893875 --- /dev/null +++ b/pixelterm @@ -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)) diff --git a/pixelterm.py b/pixelterm.py index ab4409c..1543663 100755 --- a/pixelterm.py +++ b/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)) diff --git a/pngmeta b/pngmeta new file mode 100755 index 0000000..e6d9b6b --- /dev/null +++ b/pngmeta @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import os, sys, argparse +from PIL import Image, PngImagePlugin + +parser = argparse.ArgumentParser(description='Print PNG metadata') +parser.add_argument('image', type=str) +args = parser.parse_args() +img = Image.open(args.image) +for k, v in img.info.items(): + print('{:15}: {}'.format(k, v)) diff --git a/pngmeta.py b/pngmeta.py deleted file mode 100755 index e6d9b6b..0000000 --- a/pngmeta.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -import os, sys, argparse -from PIL import Image, PngImagePlugin - -parser = argparse.ArgumentParser(description='Print PNG metadata') -parser.add_argument('image', type=str) -args = parser.parse_args() -img = Image.open(args.image) -for k, v in img.info.items(): - print('{:15}: {}'.format(k, v)) diff --git a/resolvecolor b/resolvecolor new file mode 100755 index 0000000..7dc6537 --- /dev/null +++ b/resolvecolor @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +import os, sys, argparse, os.path, json, re +from pygments.formatters import terminal256 + +formatter = terminal256.Terminal256Formatter() + +# Resolve HTML-style hex RGB color codes to xterm-256color color numbers + +if len(sys.argv) != 2: + print('Usage: resolvecolor.py #RRGGBB') + exit() + +print(formatter._closest_color(*[int(s, 16) for s in re.match('#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})', sys.argv[1]).groups()])) + diff --git a/resolvecolor.py b/resolvecolor.py deleted file mode 100755 index 7dc6537..0000000 --- a/resolvecolor.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -import os, sys, argparse, os.path, json, re -from pygments.formatters import terminal256 - -formatter = terminal256.Terminal256Formatter() - -# Resolve HTML-style hex RGB color codes to xterm-256color color numbers - -if len(sys.argv) != 2: - print('Usage: resolvecolor.py #RRGGBB') - exit() - -print(formatter._closest_color(*[int(s, 16) for s in re.match('#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})', sys.argv[1]).groups()])) - diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..983d45a --- /dev/null +++ b/setup.py @@ -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 = [], +) diff --git a/unpixelterm b/unpixelterm new file mode 100755 index 0000000..4556d07 --- /dev/null +++ b/unpixelterm @@ -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) diff --git a/unpixelterm.py b/unpixelterm.py index 416a057..234d3d1 100755 --- a/unpixelterm.py +++ b/unpixelterm.py @@ -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) -- cgit