Repository: dankamongmen/notcurses (language: python)

Issue:
sixel coredump when resizing terminal in notcurses-input
I start `notcurses-input` in a 24x80 XTerm. I then hit alt+enter, making it fullscreen. At this point, I crash with the following trace:

```c
==260939== Invalid read of size 4                                                                                                   
==260939==    at 0x48A8FDE: extract_color_table (sixel.c:432)                                                                       
==260939==    by 0x48A8FDE: sixel_blit (sixel.c:942)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Address 0x179fc200 is 0 bytes after a block of size 5,376 alloc'd                                                       
==260939==    at 0x48435FF: calloc (vg_replace_malloc.c:1117)                                                                       
==260939==    by 0x48A8E7D: sixel_blit (sixel.c:925)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x489A1B9: create_uint64_t (plot.c:578)                                                                            
==260939==    by 0x489A1B9: ncuplot_create (plot.c:661)                                                                             
==260939==    by 0x10C06E: input_demo(ncpp::NotCurses*) (input.cpp:250)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==                                                                                                                          
==260939== Invalid write of size 1                                                                                                  
==260939==    at 0x48A94A9: update_rmatrix (sixel.c:396)                                                                            
==260939==    by 0x48A94A9: extract_color_table (sixel.c:435)                                                                       
==260939==    by 0x48A94A9: sixel_blit (sixel.c:942)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Address 0x1671f0d0 is 0 bytes after a block of size 336 alloc'd                                                         
==260939==    at 0x483E7C5: malloc (vg_replace_malloc.c:380)                                                                        
==260939==    by 0x48A921B: sixel_blit (sixel.c:934)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==                                                                                                                          
==260939== Invalid write of size 4                                                                                                  
==260939==    at 0x48A94AD: extract_color_table (sixel.c:436)                                                                       
==260939==    by 0x48A94AD: sixel_blit (sixel.c:942)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Address 0x179fc200 is 0 bytes after a block of size 5,376 alloc'd                                                       
==260939==    at 0x48435FF: calloc (vg_replace_malloc.c:1117)                                                                       
==260939==    by 0x48A8E7D: sixel_blit (sixel.c:925)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x489A1B9: create_uint64_t (plot.c:578)                                                                            
==260939==    by 0x489A1B9: ncuplot_create (plot.c:661)                                                                             
==260939==    by 0x10C06E: input_demo(ncpp::NotCurses*) (input.cpp:250)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==                                                                                                                          
==260939== Invalid write of size 4
==260939==    at 0x48A9034: extract_color_table (sixel.c:446)                                                                       
==260939==    by 0x48A9034: sixel_blit (sixel.c:942)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Address 0x179fc570 is 208 bytes inside a block of size 576 free'd                                                       
==260939==    at 0x484118B: free (vg_replace_malloc.c:755)                                                                          
==260939==    by 0x48A9399: extract_color_table (sixel.c:455)                                                                       
==260939==    by 0x48A9399: sixel_blit (sixel.c:942)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Block was alloc'd at                                                                                                    
==260939==    at 0x48435FF: calloc (vg_replace_malloc.c:1117)                                                                       
==260939==    by 0x48AB9C5: sprite_wipe (sprite.c:187)                                                                              
==260939==    by 0x489ECA7: paint_sprixel (render.c:202)                                                                            
==260939==    by 0x489ECA7: paint (render.c:263)                                                                                    
==260939==    by 0x48A392C: ncpile_render_internal (render.c:1447)                                                                  
==260939==    by 0x48A392C: ncpile_render (render.c:1521)                                                                           
==260939==    by 0x48A5120: notcurses_render (render.c:1533)                                                                        
==260939==    by 0x10C10C: render (NotCurses.hh:200)                                                                                
==260939==    by 0x10C10C: input_demo(ncpp::NotCurses*) (input.cpp:263)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==                                                                                                                          
==260939== Invalid read of size 4                                                                                                   
==260939==    at 0x48A9050: extract_color_table (sixel.c:473)                                                                       
==260939==    by 0x48A9050: sixel_blit (sixel.c:942)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Address 0x179fc570 is 208 bytes inside a block of size 576 free'd                                                       
==260939==    at 0x484118B: free (vg_replace_malloc.c:755)                                                                          
==260939==    by 0x48A9399: extract_color_table (sixel.c:455)                                                                       
==260939==    by 0x48A9399: sixel_blit (sixel.c:942)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Block was alloc'd at                                                                                                    
==260939==    at 0x48435FF: calloc (vg_replace_malloc.c:1117)                                                                       
==260939==    by 0x48AB9C5: sprite_wipe (sprite.c:187)                                                                              
==260939==    by 0x489ECA7: paint_sprixel (render.c:202)                                                                            
==260939==    by 0x489ECA7: paint (render.c:263)                                                                                    
==260939==    by 0x48A392C: ncpile_render_internal (render.c:1447)                                                                  
==260939==    by 0x48A392C: ncpile_render (render.c:1521)                                                                           
==260939==    by 0x48A5120: notcurses_render (render.c:1533)                                                                        
==260939==    by 0x10C10C: render (NotCurses.hh:200)                                                                                
==260939==    by 0x10C10C: input_demo(ncpp::NotCurses*) (input.cpp:263)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==                                                                                                                          
==260939== Invalid read of size 4                                                                                                   
==260939==    at 0x48AA68E: scrub_tam_boundaries (internal.h:839)                                                                   
==260939==    by 0x48AA68E: sixel_blit_inner (sixel.c:878)                                                                          
==260939==    by 0x48AA68E: sixel_blit (sixel.c:953)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Address 0x179fc200 is 0 bytes after a block of size 5,376 alloc'd
==260939==    at 0x48435FF: calloc (vg_replace_malloc.c:1117)                                                                       
==260939==    by 0x48A8E7D: sixel_blit (sixel.c:925)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x489A1B9: create_uint64_t (plot.c:578)                                                                            
==260939==    by 0x489A1B9: ncuplot_create (plot.c:661)                                                                             
==260939==    by 0x10C06E: input_demo(ncpp::NotCurses*) (input.cpp:250)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==                                                                                                                          
==260939== Invalid read of size 4                                                                                                   
==260939==    at 0x48AA6A4: scrub_tam_boundaries (internal.h:839)                                                                   
==260939==    by 0x48AA6A4: sixel_blit_inner (sixel.c:878)                                                                          
==260939==    by 0x48AA6A4: sixel_blit (sixel.c:953)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x10C7AC: input_demo(ncpp::NotCurses*) (input.cpp:330)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)                                                                                     
==260939==  Address 0x179fc210 is 16 bytes after a block of size 5,376 alloc'd                                                      
==260939==    at 0x48435FF: calloc (vg_replace_malloc.c:1117)                                                                       
==260939==    by 0x48A8E7D: sixel_blit (sixel.c:925)                                                                                
==260939==    by 0x485A6D6: rgba_blit_dispatch (internal.h:1617)                                                                    
==260939==    by 0x485A6D6: ffmpeg_blit (ffmpeg.c:639)                                                                              
==260939==    by 0x48B183B: ncvisual_blit (visual.c:82)                                                                             
==260939==    by 0x48B3B91: ncvisual_render_pixels (visual.c:1028)                                                                  
==260939==    by 0x48B4284: ncvisual_render (visual.c:1096)                                                                         
==260939==    by 0x4896ABB: redraw_pixelplot_uint64_t (plot.c:578)                                                                  
==260939==    by 0x4897C08: redraw_plot_uint64_t (plot.c:578)                                                                       
==260939==    by 0x489A1B9: create_uint64_t (plot.c:578)                                                                            
==260939==    by 0x489A1B9: ncuplot_create (plot.c:661)                                                                             
==260939==    by 0x10C06E: input_demo(ncpp::NotCurses*) (input.cpp:250)                                                             
==260939==    by 0x10B7E4: main (input.cpp:389)    
==260939==                                                                                                                          
                                                                                                                                    
valgrind: m_mallocfree.c:278 (mk_plain_bszB): Assertion 'bszB != 0' failed.                                                         
valgrind: This is probably caused by your program erroneously writing past the                                                      
end of a heap block and corrupting heap metadata.  If you fix any                                                                   
invalid writes reported by Memcheck, this assertion failure will                                                                    
probably go away.  Please try that before reporting this as a bug.
```


Discussion / hints:
so it's pretty obvious what's happening here: our TAM geometry is changing, and we've not resized it. so we need:

* to resize the TAM when we hit the reuse code in `sixel_blit()` (and probably kitty as well -- maybe we can do this at a common point? -- `ncvisual_render_pixels()`)
* need somehow populate (all) values, despite no longer having a clear mapping (maybe run the render algorithm? hrmmm) is there some safe default?
hrmmm, is resizing on my arch laptop leading to cell-pixel geometry changes? i need that on my debian workstation to trigger the failure, which makes sense. simply resizing the screen ought not have any effect on the TAM.
i think we could eliminate a good bit of duplication actually if we did all TAM allocation in `ncvisual_render_pixels()`. we've got basically the same code in every bitmap blitter core.
yep, in the kitty implementation we have a check against the old size to ensure it's the same before reuse. i bet adding that to `sixel_blit()` would resolve this. let's see if we can't unify things, though, since we'll have something similar in fbcon and one day iTerm2.
```c
extract_color_table:417:cell: 23/11 beg: 0/0 len: 138/616                     
extract_color_table:417:cell: 23/11 beg: 0/0 len: 150/672
```

so our cell geometry seems to be the same? and yet our lengths have changed? ...strange
that explains it, though; that increased len with the same cell-pixel geom causes a bad index.
oh `cell` in that output isn't cell-pixel geometry, but cell geometry. everything makes sense then.

```
CELL GEOM!!! 25/12
CELL GEOM!!! 23/11
```
so yeah, `n->leny == rows && n->lenx == cols` is a tautology pretty much, because

```c
  int cols = bargs->u.pixel.spx->dimx;
  int rows = bargs->u.pixel.spx->dimy;
```

so we'd need update the `sprixel`'s `leny`/`lenx` values to invalidate the TAM....which we can do naturally from within `ncvisual_pixel_blit()`. so it's then natural to move the TAM allocation there, as well. good!
yeah, that invalidates it properly. in sixel, we now run into
```c
CPIX GEOM!!! 17/8 CGEOM 6/56 PGEOM 102/448                                   
COMP: 6/56 6/56                                                              
extract_color_table:417:cell: 32/16 beg: 0/0 len: 102/448                    
==1904110== Invalid read of size 4                                           
==1904110==    at 0x489C73C: sixel_wipe (sixel.c:234)                        
==1904110==    by 0x489E3C9: scrub_color_table (sixel.c:271)                 
==1904110==    by 0x489E3C9: sixel_blit (sixel.c:960)                        
==1904110==    by 0x4D1A696: rgba_blit_dispatch (internal.h:1617)            
==1904110==    by 0x4D1A696: ffmpeg_blit (ffmpeg.c:642)                      
==1904110==    by 0x48A554B: ncvisual_blit (visual.c:82)                     
==1904110==    by 0x48A780E: ncvisual_render_pixels (visual.c:1031)          
==1904110==    by 0x48A7EC4: ncvisual_render (visual.c:1099)                 
==1904110==    by 0x488B022: redraw_pixelplot_uint64_t (plot.c:578)          
==1904110==    by 0x488C0ED: redraw_plot_uint64_t (plot.c:578)               
==1904110==    by 0x10C808: input_demo(ncpp::NotCurses*) (input.cpp:350)     
==1904110==    by 0x10B7F5: main (input.cpp:409)                             
==1904110==  Address 0x0 is not stack'd, malloc'd or (recently) free'd       
==1904110==                                                                  
==1904110==                                                                  
==1904110== Process terminating with default action of signal 11 (SIGSEGV): d
==1904110==    at 0x491D75F: raise (raise.c:45)                              
==1904110==    by 0x491D8DF: ??? (in /lib/x86_64-linux-gnu/libpthread-2.32.so
==1904110==    by 0x489C73B: sixel_wipe (sixel.c:230)                        
==1904110==    by 0x489E3C9: scrub_color_table (sixel.c:271)                 
==1904110==    by 0x489E3C9: sixel_blit (sixel.c:960)                        
==1904110==    by 0x4D1A696: rgba_blit_dispatch (internal.h:1617)            
==1904110==    by 0x4D1A696: ffmpeg_blit (ffmpeg.c:642)                      
==1904110==    by 0x48A554B: ncvisual_blit (visual.c:82)                     
==1904110==    by 0x48A780E: ncvisual_render_pixels (visual.c:1031)          
==1904110==    by 0x48A7EC4: ncvisual_render (visual.c:1099)                 
==1904110==    by 0x488B022: redraw_pixelplot_uint64_t (plot.c:578)          
==1904110==    by 0x488C0ED: redraw_plot_uint64_t (plot.c:578)               
==1904110==    by 0x10C808: input_demo(ncpp::NotCurses*) (input.cpp:350)     
==1904110==    by 0x10B7F5: main (input.cpp:409)  
```
the error above is definitely a `NULL` `s->smap`
```
CPIX GEOM!!! 25/13 CGEOM 6/48 PGEOM 150/616                                  
CPIX GEOM!!! 17/8 CGEOM 6/56 PGEOM 102/448                                   
extract_color_table:418:cell: 17/8 beg: 0/0 len: 102/448
```

Fix patch:
diff --git a/src/lib/kitty.c b/src/lib/kitty.c
index 9d1fe1d3d8..a3f016973f 100644
--- a/src/lib/kitty.c
+++ b/src/lib/kitty.c
@@ -1054,47 +1054,26 @@ kitty_blit_core(ncplane* n, int linesize, const void* data, int leny, int lenx,
                 const blitterargs* bargs, ncpixelimpl_e level){
 //fprintf(stderr, "IMAGE: start %p end %p\n", data, (const char*)data + leny * linesize);
   int cols = bargs->u.pixel.spx->dimx;
-  int rows = bargs->u.pixel.spx->dimy;
   sprixel* s = bargs->u.pixel.spx;
   if(init_sprixel_animation(s)){
     return -1;
   }
-  tament* tam = NULL;
-  bool reuse = false;
-  // if we have a sprixel attached to this plane, see if we can reuse it
-  // (we need the same dimensions) and thus immediately apply its T-A table.
-  if(n->tam){
-    if(n->leny == rows && n->lenx == cols){
-      tam = n->tam;
-      reuse = true;
-    }
-  }
   int parse_start = 0;
-  if(!reuse){
-    tam = malloc(sizeof(*tam) * rows * cols);
-    if(tam == NULL){
-      goto error;
-    }
-    memset(tam, 0, sizeof(*tam) * rows * cols);
-  }
   fbuf* f = &s->glyph;
   if(write_kitty_data(f, linesize, leny, lenx, cols, data,
-                      bargs, tam, &parse_start, level)){
+                      bargs, n->tam, &parse_start, level)){
     goto error;
   }
   if(level == NCPIXEL_KITTY_STATIC){
     s->animating = false;
   }
   // take ownership of |buf| and |tam| on success.
-  if(plane_blit_sixel(s, &s->glyph, leny, lenx, parse_start, tam, SPRIXEL_UNSEEN) < 0){
+  if(plane_blit_sixel(s, &s->glyph, leny, lenx, parse_start, n->tam, SPRIXEL_UNSEEN) < 0){
     goto error;
   }
   return 1;
 
 error:
-  if(!reuse){
-    free(tam);
-  }
   fbuf_free(&s->glyph);
   return -1;
 }
diff --git a/src/lib/linux.c b/src/lib/linux.c
index 17fecf4e13..76cc8278d5 100644
--- a/src/lib/linux.c
+++ b/src/lib/linux.c
@@ -32,8 +32,6 @@ int fbcon_wipe(sprixel* s, int ycell, int xcell){
 int fbcon_blit(struct ncplane* n, int linesize, const void* data,
                int leny, int lenx, const struct blitterargs* bargs){
   uint32_t transcolor = bargs->transcolor;
-  int cols = bargs->u.pixel.spx->dimx;
-  int rows = bargs->u.pixel.spx->dimy;
   sprixel* s = bargs->u.pixel.spx;
   int cdimx = s->cellpxx;
   int cdimy = s->cellpxy;
@@ -42,21 +40,6 @@ int fbcon_blit(struct ncplane* n, int linesize, const void* data,
   if(fbuf_reserve(&s->glyph, flen)){
     return -1;
   }
-  tament* tam = NULL;
-  bool reuse = false;
-  if(n->tam){
-    if(n->leny == rows && n->lenx == cols){
-      tam = n->tam;
-      reuse = true;
-    }
-  }
-  if(!reuse){
-    tam = malloc(sizeof(*tam) * rows * cols);
-    if(tam == NULL){
-      goto error;
-    }
-    memset(tam, 0, sizeof(*tam) * rows * cols);
-  }
   for(int l = 0 ; l < leny ; ++l){
     int ycell = l / cdimy;
     size_t soffset = l * linesize;
@@ -66,32 +49,32 @@ int fbcon_blit(struct ncplane* n, int linesize, const void* data,
     for(int c = 0 ; c < lenx ; ++c){
       int xcell = c / cdimx;
       int tyx = xcell + ycell * bargs->u.pixel.spx->dimx;
-      if(tam[tyx].state >= SPRIXCELL_ANNIHILATED){
+      if(n->tam[tyx].state >= SPRIXCELL_ANNIHILATED){
         if(rgba_trans_p(*(uint32_t*)src, transcolor)){
           ncpixel_set_a((uint32_t*)src, 0); // in case it was transcolor
           if(c % cdimx == 0 && l % cdimy == 0){
-            tam[tyx].state = SPRIXCELL_ANNIHILATED_TRANS;
+            n->tam[tyx].state = SPRIXCELL_ANNIHILATED_TRANS;
           }
         }else{
-          tam[tyx].state = SPRIXCELL_ANNIHILATED;
+          n->tam[tyx].state = SPRIXCELL_ANNIHILATED;
         }
         dst[3] = 0;
         const int vyx = (l % cdimy) * cdimx + (c % cdimx);
-        tam[tyx].auxvector[vyx] = src[3];
+        n->tam[tyx].auxvector[vyx] = src[3];
       }else{
         if(rgba_trans_p(*(uint32_t*)src, transcolor)){
           ncpixel_set_a((uint32_t*)src, 0); // in case it was transcolor
           if(c % cdimx == 0 && l % cdimy == 0){
-            tam[tyx].state = SPRIXCELL_TRANSPARENT;
-          }else if(tam[tyx].state == SPRIXCELL_OPAQUE_SIXEL){
-            tam[tyx].state = SPRIXCELL_MIXED_SIXEL;
+            n->tam[tyx].state = SPRIXCELL_TRANSPARENT;
+          }else if(n->tam[tyx].state == SPRIXCELL_OPAQUE_SIXEL){
+            n->tam[tyx].state = SPRIXCELL_MIXED_SIXEL;
           }
           dst[3] = 0;
         }else{
           if(c % cdimx == 0 && l % cdimy == 0){
-            tam[tyx].state = SPRIXCELL_OPAQUE_SIXEL;
-          }else if(tam[tyx].state == SPRIXCELL_TRANSPARENT){
-            tam[tyx].state = SPRIXCELL_MIXED_SIXEL;
+            n->tam[tyx].state = SPRIXCELL_OPAQUE_SIXEL;
+          }else if(n->tam[tyx].state == SPRIXCELL_TRANSPARENT){
+            n->tam[tyx].state = SPRIXCELL_MIXED_SIXEL;
           }
           memcpy(dst + 3, src + 3, 1);
         }
@@ -103,16 +86,13 @@ int fbcon_blit(struct ncplane* n, int linesize, const void* data,
       src += 4;
     }
   }
-  scrub_tam_boundaries(tam, leny, lenx, cdimy, cdimx);
-  if(plane_blit_sixel(s, &s->glyph, leny, lenx, 0, tam, SPRIXEL_INVALIDATED) < 0){
+  scrub_tam_boundaries(n->tam, leny, lenx, cdimy, cdimx);
+  if(plane_blit_sixel(s, &s->glyph, leny, lenx, 0, n->tam, SPRIXEL_INVALIDATED) < 0){
     goto error;
   }
   return 1;
 
 error:
-  if(!reuse){
-    free(tam);
-  }
   fbuf_free(&s->glyph);
   s->glyph.size = 0;
   return -1;
diff --git a/src/lib/sixel.c b/src/lib/sixel.c
index f7ccc3b4df..5261bdfdde 100644
--- a/src/lib/sixel.c
+++ b/src/lib/sixel.c
@@ -911,38 +911,16 @@ int sixel_blit(ncplane* n, int linesize, const void* data, int leny, int lenx,
   memset(stable.deets, 0, sizeof(*stable.deets) * colorregs);
   int cols = bargs->u.pixel.spx->dimx;
   int rows = bargs->u.pixel.spx->dimy;
-  tament* tam = NULL;
-  bool reuse = false;
-  // if we have a sprixel attached to this plane, see if we can reuse it
-  // (we need the same dimensions) and thus immediately apply its T-A table.
-  if(n->tam){
-    if(n->leny == rows && n->lenx == cols){
-      tam = n->tam;
-      reuse = true;
-    }
-  }
-  if(!reuse){
-    tam = malloc(sizeof(*tam) * rows * cols);
-    if(tam == NULL){
-      sixelmap_free(stable.map);
-      free(stable.deets);
-      return -1;
-    }
-    memset(tam, 0, sizeof(*tam) * rows * cols);
-  }else{
-    typeof(bargs->u.pixel.spx->needs_refresh) rmatrix;
-    rmatrix = malloc(sizeof(*rmatrix) * rows * cols);
-    if(rmatrix == NULL){
-      sixelmap_free(stable.map);
-      free(stable.deets);
-      return -1;
-    }
-    bargs->u.pixel.spx->needs_refresh = rmatrix;
+  typeof(bargs->u.pixel.spx->needs_refresh) rmatrix;
+  rmatrix = malloc(sizeof(*rmatrix) * rows * cols);
+  if(rmatrix == NULL){
+    sixelmap_free(stable.map);
+    free(stable.deets);
+    return -1;
   }
-  if(extract_color_table(data, linesize, cols, leny, lenx, &stable, tam, bargs)){
-    if(!reuse){
-      free(tam);
-    }
+  bargs->u.pixel.spx->needs_refresh = rmatrix;
+  assert(n->tam);
+  if(extract_color_table(data, linesize, cols, leny, lenx, &stable, n->tam, bargs)){
     free(bargs->u.pixel.spx->needs_refresh);
     sixelmap_free(stable.map);
     free(stable.deets);
@@ -950,7 +928,7 @@ int sixel_blit(ncplane* n, int linesize, const void* data, int leny, int lenx,
   }
   refine_color_table(data, linesize, bargs->begy, bargs->begx, leny, lenx, &stable);
   // takes ownership of sixelmap on success
-  int r = sixel_blit_inner(leny, lenx, &stable, bargs->u.pixel.spx, tam);
+  int r = sixel_blit_inner(leny, lenx, &stable, bargs->u.pixel.spx, n->tam);
   if(r < 0){
     sixelmap_free(stable.map);
   }
diff --git a/src/lib/visual.c b/src/lib/visual.c
index f5ef6a506f..b8bff6d37e 100644
--- a/src/lib/visual.c
+++ b/src/lib/visual.c
@@ -930,6 +930,15 @@ make_sprixel_plane(notcurses* nc, ncplane* parent, ncvisual* ncv,
   return n;
 }
 
+static tament*
+create_tam(int rows, int cols){
+  tament* tam = malloc(sizeof(*tam) * rows * cols);
+  if(tam){
+    memset(tam, 0, sizeof(*tam) * rows * cols);
+  }
+  return tam;
+}
+
 // when a sprixel is blitted to a plane, that plane becomes a sprixel plane. it
 // must not be used with other output mechanisms unless erased. the plane will
 // be shrunk to fit the output, and the output is always placed at the origin.
@@ -1010,15 +1019,28 @@ ncplane* ncvisual_render_pixels(notcurses* nc, ncvisual* ncv, const struct blits
   bargs.lenx = lenx;
   bargs.flags = flags;
   bargs.u.pixel.colorregs = nc->tcache.color_registers;
+  int cols = disppixx / nc->tcache.cellpixx + !!(disppixx % nc->tcache.cellpixx);
+  int rows = outy / nc->tcache.cellpixy + !!(outy % nc->tcache.cellpixy);
   if(n->sprite == NULL){
-    int cols = disppixx / nc->tcache.cellpixx + !!(disppixx % nc->tcache.cellpixx);
-    int rows = outy / nc->tcache.cellpixy + !!(outy % nc->tcache.cellpixy);
     if((n->sprite = sprixel_alloc(&nc->tcache, n, rows, cols)) == NULL){
       ncplane_destroy(createdn);
       return NULL;
     }
+    if((n->tam = create_tam(rows, cols)) == NULL){
+      ncplane_destroy(createdn);
+      return NULL;;
+    }
   }else{
     n->sprite = sprixel_recycle(n);
+    if(n->sprite->dimy != rows || n->sprite->dimx != cols){
+      free(n->tam);
+      if((n->tam = create_tam(rows, cols)) == NULL){
+        ncplane_destroy(createdn);
+        return NULL;;
+      }
+    }
+    n->sprite->dimx = cols;
+    n->sprite->dimy = rows;
   }
   bargs.u.pixel.spx = n->sprite;
   // FIXME need to pull off the ncpile's sprixellist if anything below fails!


Test patch:
diff --git a/.github/workflows/windows_test.yml b/.github/workflows/windows_test.yml
index 4b76e9f9bf..2bdf5e8120 100644
--- a/.github/workflows/windows_test.yml
+++ b/.github/workflows/windows_test.yml
@@ -49,7 +49,7 @@ jobs:
           cd /ucrt64/lib && \
             ln -s "${HOME}/repos/QR-Code-generator/c/libqrcodegen.a" ./libqrcodegen.a
 
-      - name: cmake
+      - name: config
         run: |
           mkdir build && cd build
           cmake .. \
@@ -58,20 +58,20 @@ jobs:
             -DUSE_PANDOC=off \
             -DUSE_QRCODEGEN=on
 
-      - name: make
+      - name: build
         run: |
           cd build
           ninja
 
-      - name: ctest
+      - name: test
         run: |
           cd build
           #ctest --output-on-failure
 
-      - name: make install
+      - name: install
         run: |
           cd build
-          make install
+          ninja install
 
           #- name: python wrappers
           #run: |