From 84c7aca04e664282c1cf681b4086cc5967ce95da Mon Sep 17 00:00:00 2001 From: nick black Date: Fri, 7 May 2021 17:34:02 -0400 Subject: [PATCH] add ncblit_rgb_packed(), ncblit_rgb_loose() #1634 --- NEWS.md | 4 +++ USAGE.md | 10 ++++++ include/notcurses/notcurses.h | 10 ++++++ src/lib/blit.c | 30 +++++++++++++++- src/lib/internal.h | 4 ++- src/lib/visual.c | 64 +++++++++++++++++++++++++++++++---- 6 files changed, 114 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 751b58517..b52e7749b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,10 @@ This document attempts to list user-visible changes and any major internal rearrangements of Notcurses. +* 2.2.11 (not yet released) + * Added `ncblit_rgb_loose()` and `ncblit_rgb_packed()` helpers for blitting + 32bpp RGBx and 24bpp RGB. + * 2.2.10 (2021-05-05) * Added `NCVISUAL_OPTION_CHILDPLANE` to interpret the `n` field of `ncvisual_options` as a parent plane. diff --git a/USAGE.md b/USAGE.md index b6c462fc0..c1d234ad7 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1565,6 +1565,16 @@ Raw streams of RGBA or BGRx data can be blitted directly to an ncplane: int ncblit_rgba(const void* data, int linesize, const struct ncvisual_options* vopts); +// Same as ncblit_rgba(), but for RGBx, with 'alpha' supplied as an alpha value +// throughout, 0 <= 'alpha' <= 255. linesize ought be a multiple of 4. +int ncblit_rgb_loose(const void* data, int linesize, + const struct ncvisual_options* vopts, int alpha); + +// Same as ncblit_rgba(), but for RGB, with 'alpha' supplied as an alpha value +// throughout, 0 <= 'alpha' <= 255. linesize ought be a multiple of 3. +int ncblit_rgb_packed(const void* data, int linesize, + const struct ncvisual_options* vopts, int alpha); + // Same as ncblit_rgba(), but for BGRx. int ncblit_bgrx(const void* data, int linesize, const struct ncvisual_options* vopts); diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 4cd26db4b..30ef7b477 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -2610,6 +2610,16 @@ API int ncblit_rgba(const void* data, int linesize, API int ncblit_bgrx(const void* data, int linesize, const struct ncvisual_options* vopts); +// Supply an alpha value [0..255] to be applied throughout. linesize must be +// a multiple of 3 for this RGB data. +API int ncblit_rgb_packed(const void* data, int linesize, + const struct ncvisual_options* vopts, int alpha); + +// Supply an alpha value [0..255] to be applied throughout. linesize must be +// a multiple of 4 for this RGBx data. +API int ncblit_rgb_loose(const void* data, int linesize, + const struct ncvisual_options* vopts, int alpha); + // The ncpixel API facilitates direct management of the pixels within an // ncvisual (ncvisuals keep a backing store of 32-bit RGBA pixels, and render // them down to terminal graphics in ncvisual_render()). diff --git a/src/lib/blit.c b/src/lib/blit.c index 5e0442410..30eec1cc2 100644 --- a/src/lib/blit.c +++ b/src/lib/blit.c @@ -965,7 +965,35 @@ int ncblit_bgrx(const void* data, int linesize, const struct ncvisual_options* v if(vopts->leny <= 0 || vopts->lenx <= 0){ return -1; } - void* rdata = bgra_to_rgba(data, vopts->leny, linesize, vopts->lenx); + void* rdata = bgra_to_rgba(data, vopts->leny, &linesize, vopts->lenx, 0xff); + if(rdata == NULL){ + return -1; + } + int r = ncblit_rgba(rdata, linesize, vopts); + free(rdata); + return r; +} + +int ncblit_rgb_loose(const void* data, int linesize, + const struct ncvisual_options* vopts, int alpha){ + if(vopts->leny <= 0 || vopts->lenx <= 0){ + return -1; + } + void* rdata = rgb_loose_to_rgba(data, vopts->leny, &linesize, vopts->lenx, alpha); + if(rdata == NULL){ + return -1; + } + int r = ncblit_rgba(rdata, linesize, vopts); + free(rdata); + return r; +} + +int ncblit_rgb_packed(const void* data, int linesize, + const struct ncvisual_options* vopts, int alpha){ + if(vopts->leny <= 0 || vopts->lenx <= 0){ + return -1; + } + void* rdata = rgb_packed_to_rgba(data, vopts->leny, &linesize, vopts->lenx, alpha); if(rdata == NULL){ return -1; } diff --git a/src/lib/internal.h b/src/lib/internal.h index 4b4a36b0d..ae80e5e1d 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -1146,7 +1146,9 @@ memdup(const void* src, size_t len){ return ret; } -ALLOC void* bgra_to_rgba(const void* data, int rows, int rowstride, int cols); +ALLOC void* bgra_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha); +ALLOC void* rgb_loose_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha); +ALLOC void* rgb_packed_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha); // find the "center" cell of two lengths. in the case of even rows/columns, we // place the center on the top/left. in such a case there will be one more diff --git a/src/lib/visual.c b/src/lib/visual.c index f61e12e69..b600db3d8 100644 --- a/src/lib/visual.c +++ b/src/lib/visual.c @@ -212,23 +212,75 @@ int ncvisual_blitter_geom(const notcurses* nc, const ncvisual* n, return ret; } -void* bgra_to_rgba(const void* data, int rows, int rowstride, int cols){ - if(rowstride % 4){ // must be a multiple of 4 bytes +void* rgb_loose_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha){ + if(*rowstride % 4){ // must be a multiple of 4 bytes return NULL; } - uint32_t* ret = malloc(rowstride * rows); + if(*rowstride < cols * 4){ + return NULL; + } + uint32_t* ret = malloc(4 * cols * rows); if(ret){ for(int y = 0 ; y < rows ; ++y){ for(int x = 0 ; x < cols ; ++x){ - const uint32_t* src = (const uint32_t*)data + (rowstride / 4) * y + x; - uint32_t* dst = ret + (rowstride / 4) * y + x; - ncpixel_set_a(dst, 0xff); + const uint32_t* src = (const uint32_t*)data + (*rowstride / 4) * y + x; + uint32_t* dst = ret + cols * y + x; + ncpixel_set_a(dst, alpha); + ncpixel_set_r(dst, ncpixel_r(*src)); + ncpixel_set_g(dst, ncpixel_g(*src)); + ncpixel_set_b(dst, ncpixel_b(*src)); + } + } + } + *rowstride = cols * 4; + return ret; +} + +void* rgb_packed_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha){ + if(*rowstride % 3){ // must be a multiple of 3 bytes + return NULL; + } + if(*rowstride < cols * 3){ + return NULL; + } + uint32_t* ret = malloc(4 * cols * rows); + if(ret){ + for(int y = 0 ; y < rows ; ++y){ + for(int x = 0 ; x < cols ; ++x){ + const unsigned char* src = (const unsigned char*)data + *rowstride * y + x; + uint32_t* dst = ret + cols * y + x; + ncpixel_set_a(dst, alpha); + ncpixel_set_r(dst, src[0]); + ncpixel_set_g(dst, src[1]); + ncpixel_set_b(dst, src[2]); + } + } + } + *rowstride = cols * 4; + return ret; +} + +void* bgra_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha){ + if(*rowstride % 4){ // must be a multiple of 4 bytes + return NULL; + } + if(*rowstride < cols * 4){ + return NULL; + } + uint32_t* ret = malloc(4 * cols * rows); + if(ret){ + for(int y = 0 ; y < rows ; ++y){ + for(int x = 0 ; x < cols ; ++x){ + const uint32_t* src = (const uint32_t*)data + (*rowstride / 4) * y + x; + uint32_t* dst = ret + cols * y + x; + ncpixel_set_a(dst, alpha); ncpixel_set_r(dst, ncpixel_b(*src)); ncpixel_set_g(dst, ncpixel_g(*src)); ncpixel_set_b(dst, ncpixel_r(*src)); } } } + *rowstride = cols * 4; return ret; }