diff --git a/software/apps/bad_apple/main.c b/software/apps/bad_apple/main.c index b159a0e..3959f61 100644 --- a/software/apps/bad_apple/main.c +++ b/software/apps/bad_apple/main.c @@ -15,7 +15,7 @@ #include "test_frame.h" #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) #define FRAME_WIDTH 1280 @@ -54,8 +54,8 @@ int main() { dvi_start(&dvi0); int frame = 0; + const uint8_t *line = (const uint8_t*)MOVIE_BASE; while (true) { - const uint8_t *line = frame_bin; uint32_t *render_target; for (int y = 0; y < FRAME_HEIGHT; ++y) { uint8_t line_len = *line++; diff --git a/software/apps/bad_apple/video/mkframes.sh b/software/apps/bad_apple/video/mkframes.sh index 23d63fb..1712372 100755 --- a/software/apps/bad_apple/video/mkframes.sh +++ b/software/apps/bad_apple/video/mkframes.sh @@ -5,7 +5,7 @@ rm -rf raw rle pack.bin pack.uf2 mkdir raw ffmpeg \ - -ss 00:00:25 -to 00:01:25 \ + -ss 00:00:05.5 -to 00:00:12.5 \ -i src.mkv \ -f lavfi -i color=gray: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" \ 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 \ No newline at end of file +cat $(find rle -name "*.bin" | sort) > pack.bin + +uf2conv -f rp2040 -b 0x10010000 pack.bin -o pack.uf2 diff --git a/software/apps/bad_apple/video/packframes.mk b/software/apps/bad_apple/video/packframes.mk index 54a5a37..9b4fa81 100644 --- a/software/apps/bad_apple/video/packframes.mk +++ b/software/apps/bad_apple/video/packframes.mk @@ -5,4 +5,4 @@ RAW=$(sort $(wildcard raw/*)) all: $(patsubst raw/%.png,rle/%.bin,$(RAW)) rle/%.bin: raw/%.png - ./rle_compress.py $< $@ \ No newline at end of file + ./rle_compress.py $< $@ diff --git a/software/apps/bad_apple/video/rle_compress.py b/software/apps/bad_apple/video/rle_compress.py index 2dcb041..1a983a3 100755 --- a/software/apps/bad_apple/video/rle_compress.py +++ b/software/apps/bad_apple/video/rle_compress.py @@ -1,11 +1,5 @@ #!/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 # prebalanced symbol pairs: # @@ -25,93 +19,47 @@ import struct # on the M0+ and, because everything stays byte-aligned, it's amenable to # further compression. +import sys +from PIL import Image + def fmt_byte(a, b, n): assert(n >= 1 and n <= 64) return a << 7 | b << 6 | (n - 1 & 0x3f) -@functools.lru_cache +# row is iterable of (bool, bool) def pack_row(row): current = (False, False) current_len = 0 - pack = [] - for x in range(0, len(row), 2): - pix = row[x:x+2] + while True: + try: + pix = next(row) + except StopIteration: + if current_len > 0: + yield fmt_byte(*current, current_len) + break if pix == current or current_len == 0: current = pix current_len += 1 if pix != current: - pack.append(fmt_byte(*current, current_len)) + yield fmt_byte(*current, current_len) current = pix current_len = 1 if current_len >= 64: - pack.append(fmt_byte(*current, current_len)) + yield fmt_byte(*current, current_len) current_len = 0 - # Flush run at end of scanline - if current_len > 0: - pack.append(fmt_byte(*current, current_len)) - return bytes(pack) - -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): +def pack_image(img): assert(img.width % 2 == 0) assert(img.mode == "RGB") bimg = img.tobytes() w = img.width for y in range(img.height): - raw_row = tuple(bimg[3 * (x + w * y)] >= 0x80 for x in range(w)) - last_seen = history.last_seen(raw_row) - history.update(raw_row) - # Uncomment for row dictionary format: - # if last_seen is None: - # packed = pack_row(raw_row) - # yield struct.pack("= 0x80, bimg[3 * (x + 1 + w * y)] >= 0x80) + for x in range(0, img.width, 2))) + yield bytes((len(row),)) + yield row if __name__ == "__main__": - filelist = sorted(os.listdir(sys.argv[1])) - assert(all(x.lower().endswith(".png") for x in filelist)) - 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))) + img = Image.open(sys.argv[1]) + open(sys.argv[2], "wb").write(b"".join(pack_image(img)))