kopia lustrzana https://github.com/Wren6991/PicoDVI
Restore a bad_apple frame packing script that actually matches the RLE format in the assembly code
rodzic
6585015fa5
commit
cb7f9bcb27
|
@ -15,7 +15,7 @@
|
||||||
#include "test_frame.h"
|
#include "test_frame.h"
|
||||||
|
|
||||||
#define MOVIE_BASE (XIP_BASE + 0x10000)
|
#define MOVIE_BASE (XIP_BASE + 0x10000)
|
||||||
#define MOVIE_FRAMES 1799
|
#define MOVIE_FRAMES 209
|
||||||
|
|
||||||
// DVDD 1.25V (slower silicon may need the full 1.3, or just not work)
|
// DVDD 1.25V (slower silicon may need the full 1.3, or just not work)
|
||||||
#define FRAME_WIDTH 1280
|
#define FRAME_WIDTH 1280
|
||||||
|
@ -54,8 +54,8 @@ int main() {
|
||||||
dvi_start(&dvi0);
|
dvi_start(&dvi0);
|
||||||
|
|
||||||
int frame = 0;
|
int frame = 0;
|
||||||
|
const uint8_t *line = (const uint8_t*)MOVIE_BASE;
|
||||||
while (true) {
|
while (true) {
|
||||||
const uint8_t *line = frame_bin;
|
|
||||||
uint32_t *render_target;
|
uint32_t *render_target;
|
||||||
for (int y = 0; y < FRAME_HEIGHT; ++y) {
|
for (int y = 0; y < FRAME_HEIGHT; ++y) {
|
||||||
uint8_t line_len = *line++;
|
uint8_t line_len = *line++;
|
||||||
|
|
|
@ -5,7 +5,7 @@ rm -rf raw rle pack.bin pack.uf2
|
||||||
|
|
||||||
mkdir raw
|
mkdir raw
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
-ss 00:00:25 -to 00:01:25 \
|
-ss 00:00:05.5 -to 00:00:12.5 \
|
||||||
-i src.mkv \
|
-i src.mkv \
|
||||||
-f lavfi -i color=gray:s=1920x1080 \
|
-f lavfi -i color=gray:s=1920x1080 \
|
||||||
-f lavfi -i color=black:s=1920x1080 \
|
-f lavfi -i color=black:s=1920x1080 \
|
||||||
|
@ -13,6 +13,9 @@ ffmpeg \
|
||||||
-filter_complex "threshold,fps=30,crop=1440:1080:240:0,scale=960x720" \
|
-filter_complex "threshold,fps=30,crop=1440:1080:240:0,scale=960x720" \
|
||||||
raw/frame%04d.png
|
raw/frame%04d.png
|
||||||
|
|
||||||
./rle_compress.py raw pack.bin
|
mkdir rle
|
||||||
|
make -f packframes.mk -j$(nproc)
|
||||||
|
|
||||||
uf2conv -f rp2040 -b 0x10010000 pack.bin -o pack.uf2
|
cat $(find rle -name "*.bin" | sort) > pack.bin
|
||||||
|
|
||||||
|
uf2conv -f rp2040 -b 0x10010000 pack.bin -o pack.uf2
|
||||||
|
|
|
@ -5,4 +5,4 @@ RAW=$(sort $(wildcard raw/*))
|
||||||
all: $(patsubst raw/%.png,rle/%.bin,$(RAW))
|
all: $(patsubst raw/%.png,rle/%.bin,$(RAW))
|
||||||
|
|
||||||
rle/%.bin: raw/%.png
|
rle/%.bin: raw/%.png
|
||||||
./rle_compress.py $< $@
|
./rle_compress.py $< $@
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
|
||||||
from PIL import Image
|
|
||||||
import functools
|
|
||||||
import os
|
|
||||||
import struct
|
|
||||||
|
|
||||||
# We avoid TMDS encode on the M0+ by holding the following sequences as
|
# We avoid TMDS encode on the M0+ by holding the following sequences as
|
||||||
# prebalanced symbol pairs:
|
# prebalanced symbol pairs:
|
||||||
#
|
#
|
||||||
|
@ -25,93 +19,47 @@ import struct
|
||||||
# on the M0+ and, because everything stays byte-aligned, it's amenable to
|
# on the M0+ and, because everything stays byte-aligned, it's amenable to
|
||||||
# further compression.
|
# further compression.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
def fmt_byte(a, b, n):
|
def fmt_byte(a, b, n):
|
||||||
assert(n >= 1 and n <= 64)
|
assert(n >= 1 and n <= 64)
|
||||||
return a << 7 | b << 6 | (n - 1 & 0x3f)
|
return a << 7 | b << 6 | (n - 1 & 0x3f)
|
||||||
|
|
||||||
@functools.lru_cache
|
# row is iterable of (bool, bool)
|
||||||
def pack_row(row):
|
def pack_row(row):
|
||||||
current = (False, False)
|
current = (False, False)
|
||||||
current_len = 0
|
current_len = 0
|
||||||
pack = []
|
while True:
|
||||||
for x in range(0, len(row), 2):
|
try:
|
||||||
pix = row[x:x+2]
|
pix = next(row)
|
||||||
|
except StopIteration:
|
||||||
|
if current_len > 0:
|
||||||
|
yield fmt_byte(*current, current_len)
|
||||||
|
break
|
||||||
if pix == current or current_len == 0:
|
if pix == current or current_len == 0:
|
||||||
current = pix
|
current = pix
|
||||||
current_len += 1
|
current_len += 1
|
||||||
if pix != current:
|
if pix != current:
|
||||||
pack.append(fmt_byte(*current, current_len))
|
yield fmt_byte(*current, current_len)
|
||||||
current = pix
|
current = pix
|
||||||
current_len = 1
|
current_len = 1
|
||||||
if current_len >= 64:
|
if current_len >= 64:
|
||||||
pack.append(fmt_byte(*current, current_len))
|
yield fmt_byte(*current, current_len)
|
||||||
current_len = 0
|
current_len = 0
|
||||||
# Flush run at end of scanline
|
|
||||||
if current_len > 0:
|
|
||||||
pack.append(fmt_byte(*current, current_len))
|
|
||||||
return bytes(pack)
|
|
||||||
|
|
||||||
|
def pack_image(img):
|
||||||
class RowHistory:
|
|
||||||
|
|
||||||
def __init__(self, log_size):
|
|
||||||
self.size = 1 << log_size
|
|
||||||
# Ring buffer of last n rows we saw
|
|
||||||
self.history = [None] * self.size
|
|
||||||
self.ptr = 0
|
|
||||||
self.row_occurrences = {}
|
|
||||||
|
|
||||||
def update(self, newrow):
|
|
||||||
# First update lookups for the row we are about to evict. Note we evict rows
|
|
||||||
# one slot in *advance* (reducing effective dict size by 1) to avoid
|
|
||||||
# returning references to the slot that the new row is written to.
|
|
||||||
oldrow = self.history[(self.ptr + 1) % self.size]
|
|
||||||
if oldrow is not None:
|
|
||||||
count, lastseen = self.row_occurrences[oldrow]
|
|
||||||
if count == 1:
|
|
||||||
self.row_occurrences.pop(oldrow)
|
|
||||||
else:
|
|
||||||
self.row_occurrences[oldrow] = (count - 1, lastseen)
|
|
||||||
self.history[self.ptr] = newrow
|
|
||||||
# Then update reference count and last-seen position for the new occupant
|
|
||||||
if newrow in self.row_occurrences:
|
|
||||||
self.row_occurrences[newrow] = (self.row_occurrences[newrow][0] + 1, self.ptr)
|
|
||||||
else:
|
|
||||||
self.row_occurrences[newrow] = (1, self.ptr)
|
|
||||||
self.ptr = (self.ptr + 1) % self.size
|
|
||||||
|
|
||||||
def last_seen(self, row):
|
|
||||||
if row in self.row_occurrences:
|
|
||||||
return self.row_occurrences[row][1]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def pack_image(history, img):
|
|
||||||
assert(img.width % 2 == 0)
|
assert(img.width % 2 == 0)
|
||||||
assert(img.mode == "RGB")
|
assert(img.mode == "RGB")
|
||||||
bimg = img.tobytes()
|
bimg = img.tobytes()
|
||||||
w = img.width
|
w = img.width
|
||||||
for y in range(img.height):
|
for y in range(img.height):
|
||||||
raw_row = tuple(bimg[3 * (x + w * y)] >= 0x80 for x in range(w))
|
row = bytes(pack_row(
|
||||||
last_seen = history.last_seen(raw_row)
|
(bimg[3 * (x + w * y)] >= 0x80, bimg[3 * (x + 1 + w * y)] >= 0x80)
|
||||||
history.update(raw_row)
|
for x in range(0, img.width, 2)))
|
||||||
# Uncomment for row dictionary format:
|
yield bytes((len(row),))
|
||||||
# if last_seen is None:
|
yield row
|
||||||
# packed = pack_row(raw_row)
|
|
||||||
# yield struct.pack("<H", len(packed))
|
|
||||||
# yield packed
|
|
||||||
# else:
|
|
||||||
# yield struct.pack("<H", 0x8000 | last_seen)
|
|
||||||
packed = pack_row(raw_row)
|
|
||||||
yield bytes((len(packed),))
|
|
||||||
yield packed
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
filelist = sorted(os.listdir(sys.argv[1]))
|
img = Image.open(sys.argv[1])
|
||||||
assert(all(x.lower().endswith(".png") for x in filelist))
|
open(sys.argv[2], "wb").write(b"".join(pack_image(img)))
|
||||||
ofile = open(sys.argv[2], "wb")
|
|
||||||
history = RowHistory(log_size=15)
|
|
||||||
for fname in filelist:
|
|
||||||
print(fname)
|
|
||||||
img = Image.open(os.path.join(sys.argv[1], fname))
|
|
||||||
ofile.write(b"".join(pack_image(history, img)))
|
|
||||||
|
|
Ładowanie…
Reference in New Issue