Skip to content

Instantly share code, notes, and snippets.

@ekevoo
Created Apr 21, 2018
Embed
What would you like to do?
Console full image color
#!/usr/bin/env python3
"""
Console full image color
Usage: ./cfic image.png
Any image format supported by Pillow will work.
Make sure the image is small. Width of 80 pixels is ideal.
Keep in mind that console blocks aren't usually 1:2 exactly, and the
precise proportion varies on the font used by the terminal emulator.
The one I use (Consolas) is 12:25 and that meant I had to resize a 2:1
image to 100x48 instead of 100x50.
"""
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <http://unlicense.org>
#
from sys import stdout, argv
from PIL import Image
class Screen:
def __init__(self):
self.fore = ...
self.back = ...
self.have = set()
def __enter__(self):
self._color_reset()
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
if not exc_type:
self._color_reset()
def _color_reset(self):
stdout.write('\x1b[0m')
def next_line(self):
stdout.write('\n')
def print_char(self, top_color, bottom_color):
self._swap_to({top_color, bottom_color})
self._print_block(top_color == self.fore, bottom_color == self.fore)
def _swap_to(self, need: set):
if need <= self.have: # Yay no change required
return
elif need & self.have: # Gotta change one & keep the other
if self.fore in need:
self._change(back=next(iter(need - {self.fore})))
else:
self._change(fore=next(iter(need - {self.back})))
else: # Gotta change both
self._change(*need)
def _change(self, back=..., fore=...):
seq = []
if fore is not ...:
seq.extend((38, 2))
seq.extend(fore)
self.fore = fore
if back is not ...:
seq.extend((48, 2))
seq.extend(back)
self.back = back
if seq:
stdout.write('\x1b[%sm' % ';'.join(map(str, seq)))
self.have = {self.fore, self.back}
def _print_block(self, have_top, have_bottom):
index = (2 if have_top else 0) + (1 if have_bottom else 0)
stdout.write(' \u2584\u2580\u2588'[index])
def main(image: Image):
with Screen() as screen:
for y1 in range(0, image.height, 2):
y2 = y1 + 1
for x in range(image.width):
# Read image, ignore alpha channel
c1 = image.getpixel((x, y1))[:3]
c2 = image.getpixel((x, y2))[:3]
# Let screen figure it out
screen.print_char(c1, c2)
screen.next_line()
if __name__ == '__main__':
for _image_filename in argv[1:]:
main(Image.open(_image_filename))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment