mirror of
https://github.com/rkd77/elinks.git
synced 2024-09-28 03:06:20 -04:00
aae82861cf
To see sixel images you must: - build elinks with -Dlibsixel=true - set document.plain.sixel=1 - enable sixel in terminal options - add to mailcap: image/*; img2sixel %s; copiousoutput or image/*; convert %s sixel:- ; copiousoutput and click some images on terminal which supports sixel. While opening dialogs or menu, sixel images are not shown.
961 lines
26 KiB
C
961 lines
26 KiB
C
/** Terminal sixel routines.
|
|
* @file */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sixel.h>
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "document/document.h"
|
|
#include "osdep/osdep.h"
|
|
#include "terminal/hardio.h"
|
|
#include "terminal/screen.h"
|
|
#include "terminal/sixel.h"
|
|
#include "terminal/terminal.h"
|
|
|
|
/* encode settings object */
|
|
struct sixel_decoder {
|
|
unsigned int ref;
|
|
char *input;
|
|
char *output;
|
|
sixel_allocator_t *allocator;
|
|
};
|
|
|
|
/* encoder object */
|
|
struct sixel_encoder {
|
|
unsigned int ref; /* reference counter */
|
|
sixel_allocator_t *allocator; /* allocator object */
|
|
int reqcolors;
|
|
int color_option;
|
|
char *mapfile;
|
|
int builtin_palette;
|
|
int method_for_diffuse;
|
|
int method_for_largest;
|
|
int method_for_rep;
|
|
int quality_mode;
|
|
int method_for_resampling;
|
|
int loop_mode;
|
|
int palette_type;
|
|
int f8bit;
|
|
int finvert;
|
|
int fuse_macro;
|
|
int fignore_delay;
|
|
int complexion;
|
|
int fstatic;
|
|
int pixelwidth;
|
|
int pixelheight;
|
|
int percentwidth;
|
|
int percentheight;
|
|
int clipx;
|
|
int clipy;
|
|
int clipwidth;
|
|
int clipheight;
|
|
int clipfirst;
|
|
int macro_number;
|
|
int penetrate_multiplexer;
|
|
int encode_policy;
|
|
int ormode;
|
|
int pipe_mode;
|
|
int verbose;
|
|
int has_gri_arg_limit;
|
|
unsigned char *bgcolor;
|
|
int outfd;
|
|
int finsecure;
|
|
int *cancel_flag;
|
|
void *dither_cache;
|
|
};
|
|
|
|
/* palette type */
|
|
#define SIXEL_COLOR_OPTION_DEFAULT 0 /* use default settings */
|
|
#define SIXEL_COLOR_OPTION_MONOCHROME 1 /* use monochrome palette */
|
|
#define SIXEL_COLOR_OPTION_BUILTIN 2 /* use builtin palette */
|
|
#define SIXEL_COLOR_OPTION_MAPFILE 3 /* use mapfile option */
|
|
#define SIXEL_COLOR_OPTION_HIGHCOLOR 4 /* use highcolor option */
|
|
|
|
|
|
static int
|
|
sixel_write_callback(char *data, int size, void *priv)
|
|
{
|
|
struct string *text = priv;
|
|
|
|
add_bytes_to_string(text, data, size);
|
|
return size;
|
|
}
|
|
|
|
static SIXELSTATUS
|
|
sixel_encoder_output_without_macro(
|
|
sixel_frame_t /* in */ *frame,
|
|
sixel_dither_t /* in */ *dither,
|
|
sixel_output_t /* in */ *output,
|
|
sixel_encoder_t /* in */ *encoder)
|
|
{
|
|
SIXELSTATUS status = SIXEL_OK;
|
|
static unsigned char *p;
|
|
int depth;
|
|
enum { message_buffer_size = 256 };
|
|
char message[message_buffer_size];
|
|
int nwrite;
|
|
int dulation;
|
|
int delay;
|
|
int lag = 0;
|
|
struct timespec tv;
|
|
clock_t start;
|
|
unsigned char *pixbuf;
|
|
int width;
|
|
int height;
|
|
int pixelformat;
|
|
size_t size;
|
|
|
|
if (encoder == NULL) {
|
|
sixel_helper_set_additional_message(
|
|
"sixel_encoder_output_without_macro: encoder object is null.");
|
|
status = SIXEL_BAD_ARGUMENT;
|
|
goto end;
|
|
}
|
|
|
|
if (encoder->color_option == SIXEL_COLOR_OPTION_DEFAULT) {
|
|
sixel_dither_set_optimize_palette(dither, 1);
|
|
}
|
|
|
|
pixelformat = sixel_frame_get_pixelformat(frame);
|
|
depth = sixel_helper_compute_depth(pixelformat);
|
|
if (depth < 0) {
|
|
status = SIXEL_LOGIC_ERROR;
|
|
nwrite = sprintf(message,
|
|
"sixel_encoder_output_without_macro: "
|
|
"sixel_helper_compute_depth(%08x) failed.",
|
|
pixelformat);
|
|
if (nwrite > 0) {
|
|
sixel_helper_set_additional_message(message);
|
|
}
|
|
goto end;
|
|
}
|
|
|
|
width = sixel_frame_get_width(frame);
|
|
height = sixel_frame_get_height(frame);
|
|
size = (size_t)(width * height * depth);
|
|
p = (unsigned char *)sixel_allocator_malloc(encoder->allocator, size);
|
|
if (p == NULL) {
|
|
sixel_helper_set_additional_message(
|
|
"sixel_encoder_output_without_macro: sixel_allocator_malloc() failed.");
|
|
status = SIXEL_BAD_ALLOCATION;
|
|
goto end;
|
|
}
|
|
start = clock();
|
|
delay = sixel_frame_get_delay(frame);
|
|
if (delay > 0 && !encoder->fignore_delay) {
|
|
dulation = (int)((clock() - start) * 1000 * 1000 / CLOCKS_PER_SEC) - (int)lag;
|
|
lag = 0;
|
|
if (dulation < 10000 * delay) {
|
|
tv.tv_sec = 0;
|
|
tv.tv_nsec = (long)((10000 * delay - dulation) * 1000);
|
|
nanosleep(&tv, NULL);
|
|
} else {
|
|
lag = (int)(10000 * delay - dulation);
|
|
}
|
|
}
|
|
|
|
pixbuf = sixel_frame_get_pixels(frame);
|
|
memcpy(p, pixbuf, (size_t)(width * height * depth));
|
|
|
|
if (encoder->cancel_flag && *encoder->cancel_flag) {
|
|
goto end;
|
|
}
|
|
|
|
status = sixel_encode(p, width, height, depth, dither, output);
|
|
if (status != SIXEL_OK) {
|
|
goto end;
|
|
}
|
|
|
|
end:
|
|
sixel_allocator_free(encoder->allocator, p);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
static SIXELSTATUS
|
|
sixel_encoder_output_with_macro(
|
|
sixel_frame_t /* in */ *frame,
|
|
sixel_dither_t /* in */ *dither,
|
|
sixel_output_t /* in */ *output,
|
|
sixel_encoder_t /* in */ *encoder)
|
|
{
|
|
SIXELSTATUS status = SIXEL_OK;
|
|
enum { message_buffer_size = 256 };
|
|
char buffer[message_buffer_size];
|
|
int nwrite;
|
|
int dulation;
|
|
int lag = 0;
|
|
struct timespec tv;
|
|
clock_t start;
|
|
unsigned char *pixbuf;
|
|
int width;
|
|
int height;
|
|
int delay;
|
|
|
|
start = clock();
|
|
if (sixel_frame_get_loop_no(frame) == 0) {
|
|
if (encoder->macro_number >= 0) {
|
|
nwrite = sprintf(buffer, "\033P%d;0;1!z", encoder->macro_number);
|
|
} else {
|
|
nwrite = sprintf(buffer, "\033P%d;0;1!z", sixel_frame_get_frame_no(frame));
|
|
}
|
|
if (nwrite < 0) {
|
|
status = (SIXEL_LIBC_ERROR | (errno & 0xff));
|
|
sixel_helper_set_additional_message(
|
|
"sixel_encoder_output_with_macro: sprintf() failed.");
|
|
goto end;
|
|
}
|
|
nwrite = sixel_write_callback(buffer, (int)strlen(buffer), &encoder->outfd);
|
|
if (nwrite < 0) {
|
|
status = (SIXEL_LIBC_ERROR | (errno & 0xff));
|
|
sixel_helper_set_additional_message(
|
|
"sixel_encoder_output_with_macro: sixel_write_callback() failed.");
|
|
goto end;
|
|
}
|
|
|
|
pixbuf = sixel_frame_get_pixels(frame),
|
|
width = sixel_frame_get_width(frame),
|
|
height = sixel_frame_get_height(frame),
|
|
status = sixel_encode(pixbuf, width, height, /* unused */ 3, dither, output);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
|
|
nwrite = sixel_write_callback("\033\\", 2, &encoder->outfd);
|
|
if (nwrite < 0) {
|
|
status = (SIXEL_LIBC_ERROR | (errno & 0xff));
|
|
sixel_helper_set_additional_message(
|
|
"sixel_encoder_output_with_macro: sixel_write_callback() failed.");
|
|
goto end;
|
|
}
|
|
}
|
|
if (encoder->macro_number < 0) {
|
|
nwrite = sprintf(buffer, "\033[%d*z", sixel_frame_get_frame_no(frame));
|
|
if (nwrite < 0) {
|
|
status = (SIXEL_LIBC_ERROR | (errno & 0xff));
|
|
sixel_helper_set_additional_message(
|
|
"sixel_encoder_output_with_macro: sprintf() failed.");
|
|
}
|
|
nwrite = sixel_write_callback(buffer, (int)strlen(buffer), &encoder->outfd);
|
|
if (nwrite < 0) {
|
|
status = (SIXEL_LIBC_ERROR | (errno & 0xff));
|
|
sixel_helper_set_additional_message(
|
|
"sixel_encoder_output_with_macro: sixel_write_callback() failed.");
|
|
goto end;
|
|
}
|
|
delay = sixel_frame_get_delay(frame);
|
|
if (delay > 0 && !encoder->fignore_delay) {
|
|
dulation = (int)((clock() - start) * 1000 * 1000 / CLOCKS_PER_SEC) - (int)lag;
|
|
lag = 0;
|
|
if (dulation < 10000 * delay) {
|
|
tv.tv_sec = 0;
|
|
tv.tv_nsec = (long)((10000 * delay - dulation) * 1000);
|
|
nanosleep(&tv, NULL);
|
|
} else {
|
|
lag = (int)(10000 * delay - dulation);
|
|
}
|
|
}
|
|
}
|
|
|
|
end:
|
|
return status;
|
|
}
|
|
|
|
|
|
/* returns monochrome dithering context object */
|
|
static SIXELSTATUS
|
|
sixel_prepare_monochrome_palette(
|
|
sixel_dither_t /* out */ **dither,
|
|
int /* in */ finvert)
|
|
{
|
|
SIXELSTATUS status = SIXEL_FALSE;
|
|
|
|
if (finvert) {
|
|
*dither = sixel_dither_get(SIXEL_BUILTIN_MONO_LIGHT);
|
|
} else {
|
|
*dither = sixel_dither_get(SIXEL_BUILTIN_MONO_DARK);
|
|
}
|
|
if (*dither == NULL) {
|
|
sixel_helper_set_additional_message(
|
|
"sixel_prepare_monochrome_palette: sixel_dither_get() failed.");
|
|
status = SIXEL_RUNTIME_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
status = SIXEL_OK;
|
|
|
|
end:
|
|
return status;
|
|
}
|
|
|
|
|
|
/* returns dithering context object with specified builtin palette */
|
|
static SIXELSTATUS
|
|
sixel_prepare_builtin_palette(
|
|
sixel_dither_t /* out */ **dither,
|
|
int /* in */ builtin_palette)
|
|
{
|
|
SIXELSTATUS status = SIXEL_FALSE;
|
|
|
|
*dither = sixel_dither_get(builtin_palette);
|
|
if (*dither == NULL) {
|
|
sixel_helper_set_additional_message(
|
|
"sixel_prepare_builtin_palette: sixel_dither_get() failed.");
|
|
status = SIXEL_RUNTIME_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
status = SIXEL_OK;
|
|
|
|
end:
|
|
return status;
|
|
}
|
|
|
|
#if 0
|
|
/* create palette from specified map file */
|
|
static SIXELSTATUS
|
|
sixel_prepare_specified_palette(
|
|
sixel_dither_t /* out */ **dither,
|
|
sixel_encoder_t /* in */ *encoder)
|
|
{
|
|
SIXELSTATUS status = SIXEL_FALSE;
|
|
sixel_callback_context_for_mapfile_t callback_context;
|
|
|
|
callback_context.reqcolors = encoder->reqcolors;
|
|
callback_context.dither = NULL;
|
|
callback_context.allocator = encoder->allocator;
|
|
|
|
status = sixel_helper_load_image_file(encoder->mapfile,
|
|
1, /* fstatic */
|
|
1, /* fuse_palette */
|
|
SIXEL_PALETTE_MAX, /* reqcolors */
|
|
encoder->bgcolor,
|
|
SIXEL_LOOP_DISABLE,
|
|
load_image_callback_for_palette,
|
|
encoder->finsecure,
|
|
encoder->cancel_flag,
|
|
&callback_context,
|
|
encoder->allocator);
|
|
if (status != SIXEL_OK) {
|
|
return status;
|
|
}
|
|
|
|
*dither = callback_context.dither;
|
|
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
/* create dither object from a frame */
|
|
static SIXELSTATUS
|
|
sixel_encoder_prepare_palette(
|
|
sixel_encoder_t *encoder, /* encoder object */
|
|
sixel_frame_t *frame, /* input frame object */
|
|
sixel_dither_t **dither) /* dither object to be created from the frame */
|
|
{
|
|
SIXELSTATUS status = SIXEL_FALSE;
|
|
int histogram_colors;
|
|
|
|
switch (encoder->color_option) {
|
|
case SIXEL_COLOR_OPTION_HIGHCOLOR:
|
|
if (encoder->dither_cache) {
|
|
*dither = encoder->dither_cache;
|
|
status = SIXEL_OK;
|
|
} else {
|
|
status = sixel_dither_new(dither, (-1), encoder->allocator);
|
|
}
|
|
goto end;
|
|
case SIXEL_COLOR_OPTION_MONOCHROME:
|
|
if (encoder->dither_cache) {
|
|
*dither = encoder->dither_cache;
|
|
status = SIXEL_OK;
|
|
} else {
|
|
status = sixel_prepare_monochrome_palette(dither, encoder->finvert);
|
|
}
|
|
goto end;
|
|
case SIXEL_COLOR_OPTION_MAPFILE:
|
|
#if 0
|
|
if (encoder->dither_cache) {
|
|
*dither = encoder->dither_cache;
|
|
status = SIXEL_OK;
|
|
} else {
|
|
status = sixel_prepare_specified_palette(dither, encoder);
|
|
}
|
|
#endif
|
|
goto end;
|
|
case SIXEL_COLOR_OPTION_BUILTIN:
|
|
if (encoder->dither_cache) {
|
|
*dither = encoder->dither_cache;
|
|
status = SIXEL_OK;
|
|
} else {
|
|
status = sixel_prepare_builtin_palette(dither, encoder->builtin_palette);
|
|
}
|
|
goto end;
|
|
case SIXEL_COLOR_OPTION_DEFAULT:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_PALETTE) {
|
|
if (!sixel_frame_get_palette(frame)) {
|
|
status = SIXEL_LOGIC_ERROR;
|
|
goto end;
|
|
}
|
|
status = sixel_dither_new(dither, sixel_frame_get_ncolors(frame),
|
|
encoder->allocator);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
sixel_dither_set_palette(*dither, sixel_frame_get_palette(frame));
|
|
sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
|
|
if (sixel_frame_get_transparent(frame) != (-1)) {
|
|
sixel_dither_set_transparent(*dither, sixel_frame_get_transparent(frame));
|
|
}
|
|
if (*dither && encoder->dither_cache) {
|
|
sixel_dither_unref(encoder->dither_cache);
|
|
}
|
|
goto end;
|
|
}
|
|
|
|
if (sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_GRAYSCALE) {
|
|
switch (sixel_frame_get_pixelformat(frame)) {
|
|
case SIXEL_PIXELFORMAT_G1:
|
|
*dither = sixel_dither_get(SIXEL_BUILTIN_G1);
|
|
break;
|
|
case SIXEL_PIXELFORMAT_G2:
|
|
*dither = sixel_dither_get(SIXEL_BUILTIN_G2);
|
|
break;
|
|
case SIXEL_PIXELFORMAT_G4:
|
|
*dither = sixel_dither_get(SIXEL_BUILTIN_G4);
|
|
break;
|
|
case SIXEL_PIXELFORMAT_G8:
|
|
*dither = sixel_dither_get(SIXEL_BUILTIN_G8);
|
|
break;
|
|
default:
|
|
*dither = NULL;
|
|
status = SIXEL_LOGIC_ERROR;
|
|
goto end;
|
|
}
|
|
if (*dither && encoder->dither_cache) {
|
|
sixel_dither_unref(encoder->dither_cache);
|
|
}
|
|
sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
|
|
status = SIXEL_OK;
|
|
goto end;
|
|
}
|
|
|
|
if (encoder->dither_cache) {
|
|
sixel_dither_unref(encoder->dither_cache);
|
|
}
|
|
status = sixel_dither_new(dither, encoder->reqcolors, encoder->allocator);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
|
|
status = sixel_dither_initialize(*dither,
|
|
sixel_frame_get_pixels(frame),
|
|
sixel_frame_get_width(frame),
|
|
sixel_frame_get_height(frame),
|
|
sixel_frame_get_pixelformat(frame),
|
|
encoder->method_for_largest,
|
|
encoder->method_for_rep,
|
|
encoder->quality_mode);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
|
|
histogram_colors = sixel_dither_get_num_of_histogram_colors(*dither);
|
|
if (histogram_colors <= encoder->reqcolors) {
|
|
encoder->method_for_diffuse = SIXEL_DIFFUSE_NONE;
|
|
}
|
|
sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
|
|
|
|
status = SIXEL_OK;
|
|
|
|
end:
|
|
return status;
|
|
}
|
|
|
|
|
|
/* from libsixel-1.10.3 */
|
|
/* clip a frame with settings of specified encoder object */
|
|
static SIXELSTATUS
|
|
sixel_encoder_do_clip(
|
|
sixel_encoder_t /* in */ *encoder, /* encoder object */
|
|
sixel_frame_t /* in */ *frame) /* frame object to be resized */
|
|
{
|
|
SIXELSTATUS status = SIXEL_FALSE;
|
|
int src_width;
|
|
int src_height;
|
|
int clip_x;
|
|
int clip_y;
|
|
int clip_w;
|
|
int clip_h;
|
|
|
|
/* get frame width and height */
|
|
src_width = sixel_frame_get_width(frame);
|
|
src_height = sixel_frame_get_height(frame);
|
|
|
|
/* settings around clipping */
|
|
clip_x = encoder->clipx;
|
|
clip_y = encoder->clipy;
|
|
clip_w = encoder->clipwidth;
|
|
clip_h = encoder->clipheight;
|
|
|
|
/* adjust clipping width with comparing it to frame width */
|
|
if (clip_w + clip_x > src_width) {
|
|
if (clip_x > src_width) {
|
|
clip_w = 0;
|
|
} else {
|
|
clip_w = src_width - clip_x;
|
|
}
|
|
}
|
|
|
|
/* adjust clipping height with comparing it to frame height */
|
|
if (clip_h + clip_y > src_height) {
|
|
if (clip_y > src_height) {
|
|
clip_h = 0;
|
|
} else {
|
|
clip_h = src_height - clip_y;
|
|
}
|
|
}
|
|
|
|
/* do clipping */
|
|
if (clip_w > 0 && clip_h > 0) {
|
|
status = sixel_frame_clip(frame, clip_x, clip_y, clip_w, clip_h);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
status = SIXEL_OK;
|
|
|
|
end:
|
|
return status;
|
|
}
|
|
|
|
/* from libsixel-1.10.3 */
|
|
/* resize a frame with settings of specified encoder object */
|
|
static SIXELSTATUS
|
|
sixel_encoder_do_resize(
|
|
sixel_encoder_t /* in */ *encoder, /* encoder object */
|
|
sixel_frame_t /* in */ *frame) /* frame object to be resized */
|
|
{
|
|
SIXELSTATUS status = SIXEL_FALSE;
|
|
int src_width;
|
|
int src_height;
|
|
int dst_width;
|
|
int dst_height;
|
|
|
|
/* get frame width and height */
|
|
src_width = sixel_frame_get_width(frame);
|
|
src_height = sixel_frame_get_height(frame);
|
|
|
|
/* settings around scaling */
|
|
dst_width = encoder->pixelwidth; /* may be -1 (default) */
|
|
dst_height = encoder->pixelheight; /* may be -1 (default) */
|
|
|
|
/* if the encoder has percentwidth or percentheight property,
|
|
convert them to pixelwidth / pixelheight */
|
|
if (encoder->percentwidth > 0) {
|
|
dst_width = src_width * encoder->percentwidth / 100;
|
|
}
|
|
if (encoder->percentheight > 0) {
|
|
dst_height = src_height * encoder->percentheight / 100;
|
|
}
|
|
|
|
/* if only either width or height is set, set also the other
|
|
to retain frame aspect ratio */
|
|
if (encoder->pixelwidth > 0 && dst_height <= 0) {
|
|
dst_height = src_height * encoder->pixelwidth / src_width;
|
|
}
|
|
if (encoder->pixelheight > 0 && dst_width <= 0) {
|
|
dst_width = src_width * encoder->pixelheight / src_height;
|
|
}
|
|
|
|
/* do resize */
|
|
if (dst_width > 0 && dst_height > 0) {
|
|
status = sixel_frame_resize(frame, dst_width, dst_height,
|
|
encoder->method_for_resampling);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
status = SIXEL_OK;
|
|
|
|
end:
|
|
return status;
|
|
}
|
|
|
|
|
|
/* from libsixel-1.10.3 */
|
|
static SIXELSTATUS
|
|
sixel_encoder_encode_frame(
|
|
sixel_encoder_t *encoder,
|
|
sixel_frame_t *frame,
|
|
sixel_output_t *output)
|
|
{
|
|
SIXELSTATUS status = SIXEL_FALSE;
|
|
sixel_dither_t *dither = NULL;
|
|
//int height;
|
|
//int is_animation = 0;
|
|
//int nwrite;
|
|
|
|
/* evaluate -w, -h, and -c option: crop/scale input source */
|
|
if (encoder->clipfirst) {
|
|
/* clipping */
|
|
status = sixel_encoder_do_clip(encoder, frame);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
|
|
/* scaling */
|
|
status = sixel_encoder_do_resize(encoder, frame);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
} else {
|
|
/* scaling */
|
|
status = sixel_encoder_do_resize(encoder, frame);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
|
|
/* clipping */
|
|
status = sixel_encoder_do_clip(encoder, frame);
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* prepare dither context */
|
|
status = sixel_encoder_prepare_palette(encoder, frame, &dither);
|
|
if (status != SIXEL_OK) {
|
|
goto end;
|
|
}
|
|
|
|
if (encoder->dither_cache != NULL) {
|
|
encoder->dither_cache = dither;
|
|
sixel_dither_ref(dither);
|
|
}
|
|
|
|
#if 0
|
|
/* evaluate -v option: print palette */
|
|
if (encoder->verbose) {
|
|
if ((sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_PALETTE)) {
|
|
sixel_debug_print_palette(dither);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* evaluate -d option: set method for diffusion */
|
|
sixel_dither_set_diffusion_type(dither, encoder->method_for_diffuse);
|
|
|
|
/* evaluate -C option: set complexion score */
|
|
if (encoder->complexion > 1) {
|
|
sixel_dither_set_complexion_score(dither, encoder->complexion);
|
|
}
|
|
|
|
if (output) {
|
|
sixel_output_ref(output);
|
|
} else {
|
|
#if 0
|
|
/* create output context */
|
|
if (encoder->fuse_macro || encoder->macro_number >= 0) {
|
|
/* -u or -n option */
|
|
status = sixel_output_new(&output,
|
|
sixel_hex_write_callback,
|
|
&encoder->outfd,
|
|
encoder->allocator);
|
|
} else {
|
|
status = sixel_output_new(&output,
|
|
sixel_write_callback,
|
|
&encoder->outfd,
|
|
encoder->allocator);
|
|
}
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
sixel_output_set_8bit_availability(output, encoder->f8bit);
|
|
sixel_output_set_gri_arg_limit(output, encoder->has_gri_arg_limit);
|
|
sixel_output_set_palette_type(output, encoder->palette_type);
|
|
sixel_output_set_penetrate_multiplexer(
|
|
output, encoder->penetrate_multiplexer);
|
|
sixel_output_set_encode_policy(output, encoder->encode_policy);
|
|
sixel_output_set_ormode(output, encoder->ormode);
|
|
|
|
#if 0
|
|
if (sixel_frame_get_multiframe(frame) && !encoder->fstatic) {
|
|
if (sixel_frame_get_loop_no(frame) != 0 || sixel_frame_get_frame_no(frame) != 0) {
|
|
is_animation = 1;
|
|
}
|
|
height = sixel_frame_get_height(frame);
|
|
(void) sixel_tty_scroll(sixel_write_callback, encoder->outfd, height, is_animation);
|
|
}
|
|
#endif
|
|
|
|
if (encoder->cancel_flag && *encoder->cancel_flag) {
|
|
status = SIXEL_INTERRUPTED;
|
|
goto end;
|
|
}
|
|
|
|
/* output sixel: junction of multi-frame processing strategy */
|
|
if (encoder->fuse_macro) { /* -u option */
|
|
/* use macro */
|
|
status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
|
|
} else if (encoder->macro_number >= 0) { /* -n option */
|
|
/* use macro */
|
|
status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
|
|
} else {
|
|
/* do not use macro */
|
|
status = sixel_encoder_output_without_macro(frame, dither, output, encoder);
|
|
}
|
|
|
|
#if 0
|
|
if (encoder->cancel_flag && *encoder->cancel_flag) {
|
|
nwrite = sixel_write_callback("\x18\033\\", 3, &encoder->outfd);
|
|
if (nwrite < 0) {
|
|
status = (SIXEL_LIBC_ERROR | (errno & 0xff));
|
|
sixel_helper_set_additional_message(
|
|
"load_image_callback: sixel_write_callback() failed.");
|
|
goto end;
|
|
}
|
|
status = SIXEL_INTERRUPTED;
|
|
}
|
|
#endif
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
|
|
end:
|
|
if (output) {
|
|
sixel_output_unref(output);
|
|
}
|
|
if (dither) {
|
|
sixel_dither_unref(dither);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
void
|
|
try_to_draw_images(struct terminal *term)
|
|
{
|
|
struct image *im;
|
|
|
|
if (!term->sixel) {
|
|
return;
|
|
}
|
|
|
|
foreach (im, term->images) {
|
|
struct string text;
|
|
|
|
if (!init_string(&text)) {
|
|
return;
|
|
}
|
|
add_cursor_move_to_string(&text, im->y + 1, im->x + 1);
|
|
add_string_to_string(&text, &im->pixels);
|
|
|
|
if (text.length) {
|
|
if (term->master) want_draw();
|
|
hard_write(term->fdout, text.source, text.length);
|
|
if (term->master) done_draw();
|
|
}
|
|
done_string(&text);
|
|
}
|
|
}
|
|
|
|
void
|
|
delete_image(struct image *im)
|
|
{
|
|
del_from_list(im);
|
|
done_string(&im->pixels);
|
|
mem_free(im);
|
|
}
|
|
|
|
int
|
|
add_image_to_document(struct document *doc, struct string *pixels, int lineno)
|
|
{
|
|
unsigned char *indexed_pixels = NULL;
|
|
unsigned char *palette = NULL;
|
|
sixel_decoder_t *decoder = NULL;
|
|
sixel_frame_t *frame = NULL;
|
|
int ncolors;
|
|
int width;
|
|
int height;
|
|
int ile = 0;
|
|
struct image *im = mem_calloc(1, sizeof(*im));
|
|
SIXELSTATUS status;
|
|
|
|
if (!im) {
|
|
return 0;
|
|
}
|
|
if (!init_string(&im->pixels)) {
|
|
mem_free(im);
|
|
return 0;
|
|
}
|
|
status = sixel_decoder_new(&decoder, NULL);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
status = sixel_decode_raw(
|
|
(unsigned char *)pixels->source,
|
|
pixels->length,
|
|
&indexed_pixels,
|
|
&width,
|
|
&height,
|
|
&palette,
|
|
&ncolors,
|
|
decoder->allocator
|
|
);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
status = sixel_frame_new(&frame, decoder->allocator);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
status = sixel_frame_init(
|
|
frame,
|
|
indexed_pixels,
|
|
width,
|
|
height,
|
|
SIXEL_PIXELFORMAT_PAL8,
|
|
palette,
|
|
ncolors
|
|
);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
im->y = lineno + 1;
|
|
im->x = 0;
|
|
im->width = width;
|
|
im->height = height;
|
|
add_string_to_string(&im->pixels, pixels);
|
|
|
|
ile = (height + doc->options.cell_height - 1) / doc->options.cell_height;
|
|
add_to_list(doc->images, im);
|
|
end:
|
|
sixel_frame_unref(frame);
|
|
sixel_decoder_unref(decoder);
|
|
|
|
return ile;
|
|
}
|
|
|
|
struct image *
|
|
copy_frame(struct image *src, int box_width, int box_height, int cell_width, int cell_height, int dx, int dy)
|
|
{
|
|
sixel_decoder_t *decoder = NULL;
|
|
sixel_encoder_t *encoder = NULL;
|
|
sixel_output_t *output = NULL;
|
|
sixel_frame_t *frame = NULL;
|
|
unsigned char *indexed_pixels = NULL;
|
|
unsigned char *palette = NULL;
|
|
int ncolors;
|
|
int width;
|
|
int height;
|
|
int x;
|
|
int y;
|
|
struct image *dest = mem_calloc(1, sizeof(*dest));
|
|
SIXELSTATUS status;
|
|
|
|
if (!dest) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!init_string(&dest->pixels)) {
|
|
mem_free(dest);
|
|
return NULL;
|
|
}
|
|
status = sixel_decoder_new(&decoder, NULL);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
|
|
status = sixel_decode_raw(
|
|
(unsigned char *)src->pixels.source,
|
|
src->pixels.length,
|
|
&indexed_pixels,
|
|
&width,
|
|
&height,
|
|
&palette,
|
|
&ncolors,
|
|
decoder->allocator
|
|
);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
status = sixel_frame_new(&frame, decoder->allocator);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
status = sixel_frame_init(
|
|
frame,
|
|
indexed_pixels,
|
|
width,
|
|
height,
|
|
SIXEL_PIXELFORMAT_PAL8,
|
|
palette,
|
|
ncolors
|
|
);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
status = sixel_encoder_new(&encoder, decoder->allocator);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
x = src->x - dx;
|
|
y = src->y - dy;
|
|
encoder->clipx = x >= 0 ? 0 : (-x * cell_width);
|
|
encoder->clipy = y >= 0 ? 0 : (-y * cell_height);
|
|
encoder->clipwidth = box_width * cell_width;
|
|
encoder->clipheight = box_height * cell_height;
|
|
status = sixel_output_new(&output, sixel_write_callback, &dest->pixels, NULL);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
status = sixel_encoder_encode_frame(encoder, frame, output);
|
|
|
|
if (SIXEL_FAILED(status)) {
|
|
goto end;
|
|
}
|
|
dest->x = x < 0 ? 0 : x;
|
|
dest->y = y < 0 ? 1 : y;
|
|
dest->width = src->width;
|
|
dest->height = src->height;
|
|
end:
|
|
sixel_frame_unref(frame);
|
|
sixel_output_unref(output);
|
|
sixel_decoder_unref(decoder);
|
|
sixel_encoder_unref(encoder);
|
|
|
|
return dest;
|
|
}
|