2020-01-23 23:01:57 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2020-03-22 16:40:18 +01:00
|
|
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
# Copyright (C) 2020 Daniel Thompson
|
|
|
|
|
2020-01-28 22:17:41 +01:00
|
|
|
import argparse
|
2020-01-23 23:01:57 +01:00
|
|
|
import sys
|
2020-02-03 20:07:11 +01:00
|
|
|
import os.path
|
2020-01-23 19:55:03 +01:00
|
|
|
from PIL import Image
|
|
|
|
|
2020-02-03 20:07:11 +01:00
|
|
|
def varname(p):
|
|
|
|
return os.path.basename(os.path.splitext(p)[0])
|
|
|
|
|
2020-01-23 23:01:57 +01:00
|
|
|
def encode(im):
|
|
|
|
pixels = im.load()
|
|
|
|
|
|
|
|
rle = []
|
|
|
|
rl = 0
|
2020-01-28 22:19:06 +01:00
|
|
|
px = pixels[0, 0]
|
|
|
|
|
|
|
|
def encode_pixel(px, rl):
|
|
|
|
while rl > 255:
|
|
|
|
rle.append(255)
|
|
|
|
rle.append(0)
|
|
|
|
rl -= 255
|
|
|
|
rle.append(rl)
|
2020-01-23 23:01:57 +01:00
|
|
|
|
|
|
|
for y in range(im.height):
|
|
|
|
for x in range(im.width):
|
|
|
|
newpx = pixels[x, y]
|
|
|
|
if newpx == px:
|
2020-01-28 22:19:06 +01:00
|
|
|
rl += 1
|
|
|
|
assert(rl < (1 << 21))
|
2020-01-23 23:01:57 +01:00
|
|
|
continue
|
|
|
|
|
2020-01-28 22:19:06 +01:00
|
|
|
# Code the previous run
|
|
|
|
encode_pixel(px, rl)
|
|
|
|
|
2020-01-23 23:01:57 +01:00
|
|
|
# Start a new run
|
|
|
|
rl = 1
|
|
|
|
px = newpx
|
2020-01-28 22:19:06 +01:00
|
|
|
|
|
|
|
# Handle the final run
|
|
|
|
encode_pixel(px, rl)
|
|
|
|
|
|
|
|
return (im.width, im.height, bytes(rle))
|
|
|
|
|
|
|
|
def encode_8bit(im):
|
|
|
|
pixels = im.load()
|
|
|
|
|
|
|
|
rle = []
|
|
|
|
rl = 0
|
|
|
|
px = pixels[0, 0]
|
|
|
|
|
|
|
|
def encode_pixel(px, rl):
|
|
|
|
print(rl)
|
|
|
|
px = (px[0] & 0xe0) | ((px[1] & 0xe0) >> 3) | ((px[2] & 0xc0) >> 6)
|
|
|
|
|
|
|
|
rle.append(px)
|
|
|
|
if rl > 0:
|
|
|
|
rle.append(px)
|
|
|
|
rl -= 2
|
|
|
|
if rl > (1 << 14):
|
|
|
|
rle.append(0x80 | ((rl >> 14) & 0x7f))
|
|
|
|
if rl > (1 << 7):
|
|
|
|
rle.append(0x80 | ((rl >> 7) & 0x7f))
|
|
|
|
if rl >= 0:
|
|
|
|
rle.append( rl & 0x7f )
|
|
|
|
|
|
|
|
for y in range(im.height):
|
|
|
|
for x in range(im.width):
|
|
|
|
newpx = pixels[x, y]
|
|
|
|
if newpx == px:
|
|
|
|
rl += 1
|
|
|
|
assert(rl < (1 << 21))
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Code the previous run
|
|
|
|
encode_pixel(px, rl)
|
|
|
|
|
|
|
|
# Start a new run
|
|
|
|
rl = 1
|
|
|
|
px = newpx
|
|
|
|
|
2020-01-23 23:01:57 +01:00
|
|
|
# Handle the final run
|
2020-01-28 22:19:06 +01:00
|
|
|
encode_pixel(px, rl)
|
2020-01-23 23:01:57 +01:00
|
|
|
|
|
|
|
return (im.width, im.height, bytes(rle))
|
|
|
|
|
2020-01-28 22:17:41 +01:00
|
|
|
def render_c(image, fname):
|
|
|
|
print(f'// 1-bit RLE, generated from {fname}, {len(image[2])} bytes')
|
2020-02-03 20:07:11 +01:00
|
|
|
print(f'static const uint8_t {varname(fname)}[] = {{')
|
|
|
|
print(' ', end='')
|
2020-01-28 22:17:41 +01:00
|
|
|
i = 0
|
|
|
|
for rl in image[2]:
|
|
|
|
print(f' {hex(rl)},', end='')
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
if i == 12:
|
|
|
|
print('\n ', end='')
|
|
|
|
i = 0
|
|
|
|
|
|
|
|
print('\n};')
|
|
|
|
|
2020-01-23 23:01:57 +01:00
|
|
|
def decode_to_ascii(image):
|
|
|
|
(sx, sy, rle) = image
|
|
|
|
data = bytearray(2*sx)
|
|
|
|
dp = 0
|
|
|
|
black = ord('#')
|
|
|
|
white = ord(' ')
|
|
|
|
color = black
|
|
|
|
|
|
|
|
for rl in rle:
|
|
|
|
while rl:
|
|
|
|
data[dp] = color
|
|
|
|
data[dp+1] = color
|
|
|
|
dp += 2
|
|
|
|
rl -= 1
|
|
|
|
|
|
|
|
if dp >= (2*sx):
|
|
|
|
print(data.decode('utf-8'))
|
|
|
|
dp = 0
|
|
|
|
|
|
|
|
if color == black:
|
|
|
|
color = white
|
|
|
|
else:
|
|
|
|
color = black
|
|
|
|
|
|
|
|
# Check the image is the correct length
|
|
|
|
assert(dp == 0)
|
2020-01-23 19:55:03 +01:00
|
|
|
|
2020-01-28 22:17:41 +01:00
|
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='RLE encoder tool.')
|
|
|
|
parser.add_argument('files', nargs='+',
|
|
|
|
help='files to be encoded')
|
|
|
|
parser.add_argument('--ascii', action='store_true',
|
|
|
|
help='Run the resulting image(s) through an ascii art decoder')
|
|
|
|
parser.add_argument('--c', action='store_true',
|
|
|
|
help='Render the output as C instead of python')
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
for fname in args.files:
|
|
|
|
image = encode(Image.open(fname))
|
|
|
|
|
|
|
|
if args.c:
|
|
|
|
render_c(image, fname)
|
|
|
|
else:
|
|
|
|
print(f'# 1-bit RLE, generated from {fname}, {len(image[2])} bytes')
|
2020-02-03 20:07:11 +01:00
|
|
|
print(f'{varname(fname)} = {image}')
|
|
|
|
print()
|
2020-01-28 22:17:41 +01:00
|
|
|
|
|
|
|
if args.ascii:
|
|
|
|
print()
|
|
|
|
decode_to_ascii(image)
|