ncvisual_from_sixel() atop ncsixel_as_rgba()

This commit is contained in:
nick black 2021-12-23 08:15:18 -05:00 committed by nick black
parent bb91c170dd
commit 3d0fbc4d8f
6 changed files with 176 additions and 158 deletions

View File

@ -3382,6 +3382,9 @@ struct ncvisual* ncvisual_from_bgra(struct notcurses* nc, const void* bgra,
struct ncvisual* ncvisual_from_palidx(const void* data, int rows,
int rowstride, int cols, int palsize,
int pstride, const uint32_t* palette);
// Construct an ncvisual from a nul-terminated Sixel control sequence.
struct ncvisual* ncvisual_from_sixel(const char* s, unsigned leny, unsigned lenx);
```
`ncvisual`s can also be loaded from the contents of a plane:
@ -3635,9 +3638,6 @@ struct ncvisual* ncvisual_from_file(const char* file);
// extract the next frame from an ncvisual. returns NCERR_EOF on end of file,
// and NCERR_SUCCESS on success, otherwise some other NCERR.
int ncvisual_decode(struct ncvisual* nc);
// Convert a sixel escape into an RGBA vector.
uint32_t* ncsixel_as_rgba(const char *s, unsigned leny, unsigned lenx);
```
### Pixels

View File

@ -121,7 +121,7 @@ typedef struct ncvgeom {
**int ncplane_qrcode(struct ncplane* ***n***, unsigned* ***ymax***, unsigned* ***xmax***, const void* ***data***, size_t ***len***)**
**uint32_t* ncsixel_as_rgba(const char* ***s***, unsigned ***leny***, unsigned ***lenx***);**
**struct ncvisual* ncvisual_from_sixel(const char* ***s***, unsigned ***leny***, unsigned ***lenx***);**
# DESCRIPTION

View File

@ -3153,6 +3153,10 @@ API ALLOC struct ncvisual* ncvisual_from_plane(const struct ncplane* n,
unsigned leny, unsigned lenx)
__attribute__ ((nonnull (1)));
// Construct an ncvisual from a nul-terminated Sixel control sequence.
API ALLOC struct ncvisual* ncvisual_from_sixel(const char* s, unsigned leny, unsigned lenx)
__attribute__ ((nonnull (1)));
#define NCVISUAL_OPTION_NODEGRADE 0x0001ull // fail rather than degrade
#define NCVISUAL_OPTION_BLEND 0x0002ull // use NCALPHA_BLEND with visual
#define NCVISUAL_OPTION_HORALIGNED 0x0004ull // x is an alignment, not absolute
@ -4492,10 +4496,6 @@ API ALLOC char* notcurses_hostname(void);
// Returns a heap-allocated copy of human-readable OS name and version.
API ALLOC char* notcurses_osversion(void);
// Convert a sixel escape into an RGBA vector.
API ALLOC uint32_t* ncsixel_as_rgba(const char *s, unsigned leny, unsigned lenx)
__attribute__ ((nonnull (1)));
// Dump selected Notcurses state to the supplied 'debugfp'. Output is freeform,
// newline-delimited, and subject to change. It includes geometry of all
// planes, from all piles. No line has more than 80 columns' worth of output.

View File

@ -1125,153 +1125,3 @@ uint8_t* sixel_trans_auxvec(const ncpile* p){
}
return a;
}
uint32_t* ncsixel_as_rgba(const char *sx, unsigned leny, unsigned lenx){
#define MAXCOLORS 65535
uint32_t* rgba = malloc(sizeof(*rgba) * leny * lenx);
if(rgba == NULL){
return NULL;
}
uint32_t* colors = malloc(sizeof(*colors) * MAXCOLORS);
if(colors == NULL){
free(rgba);
return NULL;
}
// first we skip the header
while(*sx != '#'){
++sx;
}
// now we build the color table (form: #Pc;Pu;Px;Py;Pz). data starts with
// a color spec lacking a semicolon.
enum {
STATE_WANT_HASH,
STATE_WANT_COLOR,
STATE_WANT_COLORSEMI,
STATE_WANT_COLORSPACE,
STATE_WANT_DATA,
} state = STATE_WANT_HASH;
unsigned color = 0;
unsigned x = 0;
unsigned y = 0;
unsigned rle = 1;
while(*sx){
if(*sx == '\e'){
break;
}
if(state == STATE_WANT_HASH){
if('#' != *sx){
goto err;
}
state = STATE_WANT_COLOR;
}else if(state == STATE_WANT_COLOR){
if(!isdigit(*sx)){
goto err;
}
color = 0;
do{
color *= 10;
color += *sx - '0';
++sx;
}while(isdigit(*sx));
//std::cerr << "Got color " << color << std::endl;
--sx;
state = STATE_WANT_COLORSEMI;
}else if(state == STATE_WANT_COLORSEMI){
// if we get a semicolon, we're a colorspec, otherwise data
if(*sx == ';'){
state = STATE_WANT_COLORSPACE;
}else{
state = STATE_WANT_DATA;
rle = 1;
}
}else if(state == STATE_WANT_COLORSPACE){
if('2' != *(sx++)){
goto err;
}
if(';' != *(sx++)){
goto err;
}
int r = 0;
do{
r *= 10;
r += *sx - '0';
++sx;
}while(isdigit(*sx));
if(';' != *(sx++)){
goto err;
}
int g = 0;
do{
g *= 10;
g += *sx - '0';
++sx;
}while(isdigit(*sx));
if(';' != *(sx++)){
goto err;
}
int b = 0;
do{
b *= 10;
b += *sx - '0';
++sx;
}while(isdigit(*sx));
uint32_t rgb = htole(0xff000000 + (r << 16u) * 255 / 100 + (g << 8u) * 255 / 100 + b * 255 / 100);
//std::cerr << "Got color " << color << ": " << r << "/" << g << "/" << b << std::endl;
if(color >= MAXCOLORS){
goto err;
}
colors[color] = rgb;
state = STATE_WANT_HASH;
--sx;
}
// read until we hit next colorspec
if(state == STATE_WANT_DATA){
//std::cerr << "Character " << *sx << std::endl;
if(*sx == '#'){
state = STATE_WANT_HASH;
--sx;
}else if(*sx == '!'){ // RLE
++sx;
rle = 0;
do{
rle *= 10;
rle += *sx - '0';
++sx;
}while(isdigit(*sx));
if(2 >= rle){
goto err;
}
--sx;
}else if(*sx == '$'){
x = 0;
state = STATE_WANT_HASH;
}else if(*sx == '-'){
x = 0;
y += 6;
state = STATE_WANT_HASH;
}else{
//std::cerr << "RLE: " << rle << " pos: " << y << "*" << x << std::endl;
for(unsigned xpos = x ; xpos < x + rle ; ++xpos){
for(unsigned ypos = y ; ypos < y + 6 ; ++ypos){
if((*sx - 63) & (1u << (ypos - y))){
// ought be an empty pixel
//std::cerr << *s << " BMAP[" << ypos << "][" << xpos << "] = " << std::hex << colors[color] << std::dec << std::endl;
rgba[ypos * lenx + xpos] = colors[color];
}
}
}
x += rle;
rle = 1;
}
}
++sx;
}
free(colors);
return rgba;
err:
free(rgba);
free(colors);
return NULL;
#undef MAXCOLORS
}

167
src/lib/sixel.h Normal file
View File

@ -0,0 +1,167 @@
#ifndef NOTCURSES_SIXEL
#define NOTCURSES_SIXEL
#ifdef __cplusplus
extern "C" {
#endif
#include <ctype.h>
#include <stdlib.h>
uint32_t* ncsixel_as_rgba(const char *sx, unsigned leny, unsigned lenx){
#define MAXCOLORS 65535
// cast is necessary for c++ callers
uint32_t* rgba = (typeof rgba)malloc(sizeof(*rgba) * leny * lenx);
if(rgba == NULL){
return NULL;
}
// cast is necessary for c++ callers
uint32_t* colors = (typeof colors)malloc(sizeof(*colors) * MAXCOLORS);
if(colors == NULL){
free(rgba);
return NULL;
}
// first we skip the header
while(*sx != '#'){
++sx;
}
// now we build the color table (form: #Pc;Pu;Px;Py;Pz). data starts with
// a color spec lacking a semicolon.
enum {
STATE_WANT_HASH,
STATE_WANT_COLOR,
STATE_WANT_COLORSEMI,
STATE_WANT_COLORSPACE,
STATE_WANT_DATA,
} state = STATE_WANT_HASH;
unsigned color = 0;
unsigned x = 0;
unsigned y = 0;
unsigned rle = 1;
while(*sx){
if(*sx == '\e'){
break;
}
if(state == STATE_WANT_HASH){
if('#' != *sx){
goto err;
}
state = STATE_WANT_COLOR;
}else if(state == STATE_WANT_COLOR){
if(!isdigit(*sx)){
goto err;
}
color = 0;
do{
color *= 10;
color += *sx - '0';
++sx;
}while(isdigit(*sx));
//std::cerr << "Got color " << color << std::endl;
--sx;
state = STATE_WANT_COLORSEMI;
}else if(state == STATE_WANT_COLORSEMI){
// if we get a semicolon, we're a colorspec, otherwise data
if(*sx == ';'){
state = STATE_WANT_COLORSPACE;
}else{
state = STATE_WANT_DATA;
rle = 1;
}
}else if(state == STATE_WANT_COLORSPACE){
if('2' != *(sx++)){
goto err;
}
if(';' != *(sx++)){
goto err;
}
int r = 0;
do{
r *= 10;
r += *sx - '0';
++sx;
}while(isdigit(*sx));
if(';' != *(sx++)){
goto err;
}
int g = 0;
do{
g *= 10;
g += *sx - '0';
++sx;
}while(isdigit(*sx));
if(';' != *(sx++)){
goto err;
}
int b = 0;
do{
b *= 10;
b += *sx - '0';
++sx;
}while(isdigit(*sx));
uint32_t rgb = htole(0xff000000 + (r << 16u) * 255 / 100 + (g << 8u) * 255 / 100 + b * 255 / 100);
//std::cerr << "Got color " << color << ": " << r << "/" << g << "/" << b << std::endl;
if(color >= MAXCOLORS){
goto err;
}
colors[color] = rgb;
state = STATE_WANT_HASH;
--sx;
}
// read until we hit next colorspec
if(state == STATE_WANT_DATA){
//std::cerr << "Character " << *sx << std::endl;
if(*sx == '#'){
state = STATE_WANT_HASH;
--sx;
}else if(*sx == '!'){ // RLE
++sx;
rle = 0;
do{
rle *= 10;
rle += *sx - '0';
++sx;
}while(isdigit(*sx));
if(2 >= rle){
goto err;
}
--sx;
}else if(*sx == '$'){
x = 0;
state = STATE_WANT_HASH;
}else if(*sx == '-'){
x = 0;
y += 6;
state = STATE_WANT_HASH;
}else{
//std::cerr << "RLE: " << rle << " pos: " << y << "*" << x << std::endl;
for(unsigned xpos = x ; xpos < x + rle ; ++xpos){
for(unsigned ypos = y ; ypos < y + 6 ; ++ypos){
if((*sx - 63) & (1u << (ypos - y))){
// ought be an empty pixel
//std::cerr << *s << " BMAP[" << ypos << "][" << xpos << "] = " << std::hex << colors[color] << std::dec << std::endl;
rgba[ypos * lenx + xpos] = colors[color];
}
}
}
x += rle;
rle = 1;
}
}
++sx;
}
free(colors);
return rgba;
err:
free(rgba);
free(colors);
return NULL;
#undef MAXCOLORS
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -2,6 +2,7 @@
#include "lib/visual-details.h"
#include <vector>
#include <iostream>
#include "lib/sixel.h"
/*
void print_bmap(const std::vector<uint32_t> rgba, int pixy, int pixx){