mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
ncvisual_from_sixel() atop ncsixel_as_rgba()
This commit is contained in:
parent
bb91c170dd
commit
3d0fbc4d8f
6
USAGE.md
6
USAGE.md
@ -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,
|
struct ncvisual* ncvisual_from_palidx(const void* data, int rows,
|
||||||
int rowstride, int cols, int palsize,
|
int rowstride, int cols, int palsize,
|
||||||
int pstride, const uint32_t* palette);
|
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:
|
`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,
|
// extract the next frame from an ncvisual. returns NCERR_EOF on end of file,
|
||||||
// and NCERR_SUCCESS on success, otherwise some other NCERR.
|
// and NCERR_SUCCESS on success, otherwise some other NCERR.
|
||||||
int ncvisual_decode(struct ncvisual* nc);
|
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
|
### Pixels
|
||||||
|
@ -121,7 +121,7 @@ typedef struct ncvgeom {
|
|||||||
|
|
||||||
**int ncplane_qrcode(struct ncplane* ***n***, unsigned* ***ymax***, unsigned* ***xmax***, const void* ***data***, size_t ***len***)**
|
**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
|
# DESCRIPTION
|
||||||
|
|
||||||
|
@ -3153,6 +3153,10 @@ API ALLOC struct ncvisual* ncvisual_from_plane(const struct ncplane* n,
|
|||||||
unsigned leny, unsigned lenx)
|
unsigned leny, unsigned lenx)
|
||||||
__attribute__ ((nonnull (1)));
|
__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_NODEGRADE 0x0001ull // fail rather than degrade
|
||||||
#define NCVISUAL_OPTION_BLEND 0x0002ull // use NCALPHA_BLEND with visual
|
#define NCVISUAL_OPTION_BLEND 0x0002ull // use NCALPHA_BLEND with visual
|
||||||
#define NCVISUAL_OPTION_HORALIGNED 0x0004ull // x is an alignment, not absolute
|
#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.
|
// Returns a heap-allocated copy of human-readable OS name and version.
|
||||||
API ALLOC char* notcurses_osversion(void);
|
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,
|
// Dump selected Notcurses state to the supplied 'debugfp'. Output is freeform,
|
||||||
// newline-delimited, and subject to change. It includes geometry of all
|
// 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.
|
// planes, from all piles. No line has more than 80 columns' worth of output.
|
||||||
|
150
src/lib/sixel.c
150
src/lib/sixel.c
@ -1125,153 +1125,3 @@ uint8_t* sixel_trans_auxvec(const ncpile* p){
|
|||||||
}
|
}
|
||||||
return a;
|
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
167
src/lib/sixel.h
Normal 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
|
@ -2,6 +2,7 @@
|
|||||||
#include "lib/visual-details.h"
|
#include "lib/visual-details.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include "lib/sixel.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void print_bmap(const std::vector<uint32_t> rgba, int pixy, int pixx){
|
void print_bmap(const std::vector<uint32_t> rgba, int pixy, int pixx){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user