From 0d277f1d91f755aecafdcb6145eddf4f17fb8db8 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 19 Sep 2024 01:24:27 -0700 Subject: vcr: densify+deduplicate mem_to_png palette This introduces a LUT indirection table for mapping the raw layer values to a dense, deduplicated palette used with the PNG. This should help with compression ratios at basically no cost. Before `1800x8000 --headless --snapshot 10 --hertz 60`: -rw-r--r-- 1 vc vc 87873 Sep 19 01:36 09.19.24-01:36:43-0.png -rw-r--r-- 1 vc vc 215719 Sep 19 01:36 09.19.24-01:36:43-1.png -rw-r--r-- 1 vc vc 219323 Sep 19 01:37 09.19.24-01:36:43-2.png -rw-r--r-- 1 vc vc 221979 Sep 19 01:37 09.19.24-01:36:43-3.png After: -rw-r--r-- 1 vc vc 72303 Sep 19 01:37 09.19.24-01:37:30-0.png -rw-r--r-- 1 vc vc 174100 Sep 19 01:37 09.19.24-01:37:30-1.png -rw-r--r-- 1 vc vc 177430 Sep 19 01:37 09.19.24-01:37:30-2.png -rw-r--r-- 1 vc vc 178711 Sep 19 01:38 09.19.24-01:37:30-3.png Without any increase in compression level used. Which while it would improve ratios further, substantially increases CPU cost. --- src/vcr.c | 100 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/src/vcr.c b/src/vcr.c index 6c4fa8e..c1edaed 100644 --- a/src/vcr.c +++ b/src/vcr.c @@ -2034,53 +2034,77 @@ static int vcr_present_xlib_to_png(vcr_t *vcr, vcr_dest_t *dest) #define VCR_PNG_DARK_RED {0x80, 0x00, 0x00} #define VCR_PNG_DARK_CYAN {0x00, 0x5b, 0x5b} +enum { + VCR_LUT_BLACK = 0, + VCR_LUT_WHITE, + VCR_LUT_RED, + VCR_LUT_CYAN, + VCR_LUT_DARK_GRAY, + VCR_LUT_DARKER_GRAY, + VCR_LUT_DARK_WHITE, + VCR_LUT_DARK_RED, + VCR_LUT_DARK_CYAN, +}; + static int vcr_present_mem_to_png(vcr_t *vcr, vcr_dest_t *dest) { - static png_color pal[256] = { /* programming gfx like it's 1990 can be such a joy */ + static png_color pal[] = { /* programming gfx like it's 1990 can be such a joy */ + [VCR_LUT_BLACK] = {}, + [VCR_LUT_WHITE] = VCR_PNG_WHITE, + [VCR_LUT_RED] = VCR_PNG_RED, + [VCR_LUT_CYAN] = VCR_PNG_CYAN, + [VCR_LUT_DARK_GRAY] = VCR_PNG_DARK_GRAY, + [VCR_LUT_DARKER_GRAY] = VCR_PNG_DARKER_GRAY, + [VCR_LUT_DARK_WHITE] = VCR_PNG_DARK_WHITE, + [VCR_LUT_DARK_RED] = VCR_PNG_DARK_RED, + [VCR_LUT_DARK_CYAN] = VCR_PNG_DARK_CYAN, + }; + /* lut is an indirection table for mapping layer bit combinations to the above deduplicated denser color palette */ + static uint8_t lut[256] = { /* text solid white above all layers */ - [VCR_TEXT] = VCR_PNG_WHITE, - [VCR_TEXT_SEP] = VCR_PNG_WHITE, - [VCR_TEXT_SEP_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_GRAPHA] = VCR_PNG_WHITE, - [VCR_TEXT_GRAPHB] = VCR_PNG_WHITE, - [VCR_TEXT_GRAPHAB] = VCR_PNG_WHITE, - [VCR_TEXT_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_GRAPHA_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_GRAPHB_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_GRAPHAB_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_ODD] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_SEP] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_SEP_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_GRAPHA] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_GRAPHB] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_GRAPHAB] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_GRAPHA_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_GRAPHB_SHADOW] = VCR_PNG_WHITE, - [VCR_TEXT_ODD_GRAPHAB_SHADOW] = VCR_PNG_WHITE, + [VCR_TEXT] = VCR_LUT_WHITE, + [VCR_TEXT_SEP] = VCR_LUT_WHITE, + [VCR_TEXT_SEP_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_GRAPHA] = VCR_LUT_WHITE, + [VCR_TEXT_GRAPHB] = VCR_LUT_WHITE, + [VCR_TEXT_GRAPHAB] = VCR_LUT_WHITE, + [VCR_TEXT_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_GRAPHA_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_GRAPHB_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_GRAPHAB_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_ODD] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_SEP] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_SEP_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_GRAPHA] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_GRAPHB] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_GRAPHAB] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_GRAPHA_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_GRAPHB_SHADOW] = VCR_LUT_WHITE, + [VCR_TEXT_ODD_GRAPHAB_SHADOW] = VCR_LUT_WHITE, /* no shadow or text, plain graph colors */ - [VCR_GRAPHA] = VCR_PNG_RED, - [VCR_GRAPHB] = VCR_PNG_CYAN, - [VCR_GRAPHAB] = VCR_PNG_WHITE, - [VCR_GRAPHA_ODD] = VCR_PNG_RED, - [VCR_GRAPHB_ODD] = VCR_PNG_CYAN, - [VCR_GRAPHAB_ODD] = VCR_PNG_WHITE, + [VCR_GRAPHA] = VCR_LUT_RED, + [VCR_GRAPHB] = VCR_LUT_CYAN, + [VCR_GRAPHAB] = VCR_LUT_WHITE, + [VCR_GRAPHA_ODD] = VCR_LUT_RED, + [VCR_GRAPHB_ODD] = VCR_LUT_CYAN, + [VCR_GRAPHAB_ODD] = VCR_LUT_WHITE, /* shadowed same but dark */ - [VCR_SHADOW_GRAPHA] = VCR_PNG_DARK_RED, - [VCR_SHADOW_GRAPHB] = VCR_PNG_DARK_CYAN, - [VCR_SHADOW_GRAPHAB] = VCR_PNG_DARK_WHITE, - [VCR_SHADOW_ODD_GRAPHA] = VCR_PNG_DARK_RED, - [VCR_SHADOW_ODD_GRAPHB] = VCR_PNG_DARK_CYAN, - [VCR_SHADOW_ODD_GRAPHAB] = VCR_PNG_DARK_WHITE, + [VCR_SHADOW_GRAPHA] = VCR_LUT_DARK_RED, + [VCR_SHADOW_GRAPHB] = VCR_LUT_DARK_CYAN, + [VCR_SHADOW_GRAPHAB] = VCR_LUT_DARK_WHITE, + [VCR_SHADOW_ODD_GRAPHA] = VCR_LUT_DARK_RED, + [VCR_SHADOW_ODD_GRAPHB] = VCR_LUT_DARK_CYAN, + [VCR_SHADOW_ODD_GRAPHAB] = VCR_LUT_DARK_WHITE, /* the rest get defaulted to black, which is great. */ - [VCR_SEP] = VCR_PNG_DARK_GRAY, - [VCR_ODD] = VCR_PNG_DARKER_GRAY, - [VCR_SEP_ODD] = VCR_PNG_DARK_GRAY, + [VCR_SEP] = VCR_LUT_DARK_GRAY, + [VCR_ODD] = VCR_LUT_DARKER_GRAY, + [VCR_SEP_ODD] = VCR_LUT_DARK_GRAY, }; png_bytepp row_pointers; @@ -2148,7 +2172,7 @@ static int vcr_present_mem_to_png(vcr_t *vcr, vcr_dest_t *dest) unsigned sg_shift = (phase_k_mod_width & 0x1) << 2; uint8_t *sg = &vcr->mem.bits[(i * VCR_ROW_HEIGHT + j) * vcr->mem.pitch + (phase_k_mod_width >> 1)]; - *d = (*s & (~mask & 0xf)) | ((*sg & (mask << sg_shift)) >> sg_shift) | border | odd; + *d = lut[(*s & (~mask & 0xf)) | ((*sg & (mask << sg_shift)) >> sg_shift) | border | odd]; /* this copy pasta unrolls the loop to unpack two pixels from the nibbles at a time */ d++; @@ -2162,7 +2186,7 @@ static int vcr_present_mem_to_png(vcr_t *vcr, vcr_dest_t *dest) sg_shift = (phase_k_mod_width & 0x1) << 2; sg = &vcr->mem.bits[(i * VCR_ROW_HEIGHT + j) * vcr->mem.pitch + (phase_k_mod_width >> 1)]; - *d = ((*s & ~(mask << 4)) >> 4) | ((*sg & (mask << sg_shift)) >> sg_shift) | border | odd; + *d = lut[((*s & ~(mask << 4)) >> 4) | ((*sg & (mask << sg_shift)) >> sg_shift) | border | odd]; } } -- cgit v1.2.3