#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_SYS_UNISTD_H
# include <sys/unistd.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_TIME_H
# include <time.h>
#endif
#if HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#if HAVE_ERRNO_H
# include <errno.h>
#endif
#if HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#if HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include <sixel.h>
#include "tty.h"
#include "encoder.h"
#include "rgblookup.h"
static char *
arg_strdup(
char const *s,
sixel_allocator_t *allocator)
{
char *p;
p = (char *)sixel_allocator_malloc(allocator, strlen(s) + 1);
if (p) {
strcpy(p, s);
}
return p;
}
static SIXELSTATUS
sixel_parse_x_colorspec(
unsigned char **bgcolor,
char const *s,
sixel_allocator_t *allocator)
{
SIXELSTATUS status = SIXEL_FALSE;
char *p;
unsigned char components[3];
int component_index = 0;
unsigned long v;
char *endptr;
char *buf = NULL;
struct color const *pcolor;
pcolor = lookup_rgb(s, strlen(s));
if (pcolor) {
*bgcolor = (unsigned char *)sixel_allocator_malloc(allocator, 3);
if (*bgcolor == NULL) {
sixel_helper_set_additional_message(
"sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
(*bgcolor)[0] = pcolor->r;
(*bgcolor)[1] = pcolor->g;
(*bgcolor)[2] = pcolor->b;
} else if (s[0] == 'r' && s[1] == 'g' && s[2] == 'b' && s[3] == ':') {
p = buf = arg_strdup(s + 4, allocator);
if (buf == NULL) {
sixel_helper_set_additional_message(
"sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
while (*p) {
v = 0;
for (endptr = p; endptr - p <= 12; ++endptr) {
if (*endptr >= '0' && *endptr <= '9') {
v = (v << 4) | (unsigned long)(*endptr - '0');
} else if (*endptr >= 'a' && *endptr <= 'f') {
v = (v << 4) | (unsigned long)(*endptr - 'a' + 10);
} else if (*endptr >= 'A' && *endptr <= 'F') {
v = (v << 4) | (unsigned long)(*endptr - 'A' + 10);
} else {
break;
}
}
if (endptr - p == 0) {
break;
}
if (endptr - p > 4) {
break;
}
v = v << ((4 - (endptr - p)) * 4) >> 8;
components[component_index++] = (unsigned char)v;
p = endptr;
if (component_index == 3) {
break;
}
if (*p == '\0') {
break;
}
if (*p != '/') {
break;
}
++p;
}
if (component_index != 3 || *p != '\0' || *p == '/') {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
*bgcolor = (unsigned char *)sixel_allocator_malloc(allocator, 3);
if (*bgcolor == NULL) {
sixel_helper_set_additional_message(
"sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
(*bgcolor)[0] = components[0];
(*bgcolor)[1] = components[1];
(*bgcolor)[2] = components[2];
} else if (*s == '#') {
buf = arg_strdup(s + 1, allocator);
if (buf == NULL) {
sixel_helper_set_additional_message(
"sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
for (p = endptr = buf; endptr - p <= 12; ++endptr) {
if (*endptr >= '0' && *endptr <= '9') {
*endptr -= '0';
} else if (*endptr >= 'a' && *endptr <= 'f') {
*endptr -= 'a' - 10;
} else if (*endptr >= 'A' && *endptr <= 'F') {
*endptr -= 'A' - 10;
} else if (*endptr == '\0') {
break;
} else {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
}
if (endptr - p > 12) {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
*bgcolor = (unsigned char *)sixel_allocator_malloc(allocator, 3);
if (*bgcolor == NULL) {
sixel_helper_set_additional_message(
"sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
switch (endptr - p) {
case 3:
(*bgcolor)[0] = (unsigned char)(p[0] << 4);
(*bgcolor)[1] = (unsigned char)(p[1] << 4);
(*bgcolor)[2] = (unsigned char)(p[2] << 4);
break;
case 6:
(*bgcolor)[0] = (unsigned char)(p[0] << 4 | p[1]);
(*bgcolor)[1] = (unsigned char)(p[2] << 4 | p[3]);
(*bgcolor)[2] = (unsigned char)(p[4] << 4 | p[4]);
break;
case 9:
(*bgcolor)[0] = (unsigned char)(p[0] << 4 | p[1]);
(*bgcolor)[1] = (unsigned char)(p[3] << 4 | p[4]);
(*bgcolor)[2] = (unsigned char)(p[6] << 4 | p[7]);
break;
case 12:
(*bgcolor)[0] = (unsigned char)(p[0] << 4 | p[1]);
(*bgcolor)[1] = (unsigned char)(p[4] << 4 | p[5]);
(*bgcolor)[2] = (unsigned char)(p[8] << 4 | p[9]);
break;
default:
status = SIXEL_BAD_ARGUMENT;
goto end;
}
} else {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
status = SIXEL_OK;
end:
sixel_allocator_free(allocator, buf);
return status;
}
static int
sixel_write_callback(char *data, int size, void *priv)
{
int result;
#if defined(__MINGW64__)
result = write(*(int *)priv, data, (unsigned int)size);
#else
result = write(*(int *)priv, data, (size_t)size);
#endif
return result;
}
static int
sixel_hex_write_callback(
char *data,
int size,
void *priv)
{
char hex[SIXEL_OUTPUT_PACKET_SIZE * 2];
int i;
int j;
int result;
for (i = j = 0; i < size; ++i, ++j) {
hex[j] = (data[i] >> 4) & 0xf;
hex[j] += (hex[j] < 10 ? '0': ('a' - 10));
hex[++j] = data[i] & 0xf;
hex[j] += (hex[j] < 10 ? '0': ('a' - 10));
}
#if defined(__MINGW64__)
result = write(*(int *)priv, hex, (unsigned int)(size * 2));
#else
result = write(*(int *)priv, hex, (size_t)(size * 2));
#endif
return result;
}
static SIXELSTATUS
sixel_prepare_monochrome_palette(
sixel_dither_t **dither,
int 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;
}
static SIXELSTATUS
sixel_prepare_builtin_palette(
sixel_dither_t **dither,
int 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;
}
typedef struct sixel_callback_context_for_mapfile {
int reqcolors;
sixel_dither_t *dither;
sixel_allocator_t *allocator;
} sixel_callback_context_for_mapfile_t;
static SIXELSTATUS
load_image_callback_for_palette(
sixel_frame_t *frame,
void *data)
{
SIXELSTATUS status = SIXEL_FALSE;
sixel_callback_context_for_mapfile_t *callback_context;
callback_context = (sixel_callback_context_for_mapfile_t *)data;
switch (sixel_frame_get_pixelformat(frame)) {
case SIXEL_PIXELFORMAT_PAL1:
case SIXEL_PIXELFORMAT_PAL2:
case SIXEL_PIXELFORMAT_PAL4:
case SIXEL_PIXELFORMAT_PAL8:
if (sixel_frame_get_palette(frame) == NULL) {
status = SIXEL_LOGIC_ERROR;
goto end;
}
status = sixel_dither_new(
&callback_context->dither,
sixel_frame_get_ncolors(frame),
callback_context->allocator);
if (SIXEL_FAILED(status)) {
goto end;
}
sixel_dither_set_palette(callback_context->dither,
sixel_frame_get_palette(frame));
status = SIXEL_OK;
break;
case SIXEL_PIXELFORMAT_G1:
callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G1);
status = SIXEL_OK;
break;
case SIXEL_PIXELFORMAT_G2:
callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G1);
callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G2);
status = SIXEL_OK;
break;
case SIXEL_PIXELFORMAT_G4:
callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G4);
status = SIXEL_OK;
break;
case SIXEL_PIXELFORMAT_G8:
callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G8);
status = SIXEL_OK;
break;
default:
status = sixel_dither_new(
&callback_context->dither,
callback_context->reqcolors,
callback_context->allocator);
if (SIXEL_FAILED(status)) {
goto end;
}
status = sixel_dither_initialize(callback_context->dither,
sixel_frame_get_pixels(frame),
sixel_frame_get_width(frame),
sixel_frame_get_height(frame),
sixel_frame_get_pixelformat(frame),
SIXEL_LARGE_NORM,
SIXEL_REP_CENTER_BOX,
SIXEL_QUALITY_HIGH);
if (SIXEL_FAILED(status)) {
sixel_dither_unref(callback_context->dither);
goto end;
}
status = SIXEL_OK;
break;
}
end:
return status;
}
static SIXELSTATUS
sixel_prepare_specified_palette(
sixel_dither_t **dither,
sixel_encoder_t *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,
1,
256,
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;
}
static SIXELSTATUS
sixel_encoder_prepare_palette(
sixel_encoder_t *encoder,
sixel_frame_t *frame,
sixel_dither_t **dither)
{
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 (encoder->dither_cache) {
*dither = encoder->dither_cache;
status = SIXEL_OK;
} else {
status = sixel_prepare_specified_palette(dither, encoder);
}
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)) {
sixel_dither_unref(*dither);
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;
}
static SIXELSTATUS
sixel_encoder_do_resize(
sixel_encoder_t *encoder,
sixel_frame_t *frame)
{
SIXELSTATUS status = SIXEL_FALSE;
int src_width;
int src_height;
int dst_width;
int dst_height;
src_width = sixel_frame_get_width(frame);
src_height = sixel_frame_get_height(frame);
dst_width = encoder->pixelwidth;
dst_height = encoder->pixelheight;
if (encoder->percentwidth > 0) {
dst_width = src_width * encoder->percentwidth / 100;
}
if (encoder->percentheight > 0) {
dst_height = src_height * encoder->percentheight / 100;
}
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;
}
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;
}
}
status = SIXEL_OK;
end:
return status;
}
static SIXELSTATUS
sixel_encoder_do_clip(
sixel_encoder_t *encoder,
sixel_frame_t *frame)
{
SIXELSTATUS status = SIXEL_FALSE;
int src_width;
int src_height;
int clip_x;
int clip_y;
int clip_w;
int clip_h;
src_width = sixel_frame_get_width(frame);
src_height = sixel_frame_get_height(frame);
clip_x = encoder->clipx;
clip_y = encoder->clipy;
clip_w = encoder->clipwidth;
clip_h = encoder->clipheight;
if (clip_w + clip_x > src_width) {
if (clip_x > src_width) {
clip_w = 0;
} else {
clip_w = src_width - clip_x;
}
}
if (clip_h + clip_y > src_height) {
if (clip_y > src_height) {
clip_h = 0;
} else {
clip_h = src_height - clip_y;
}
}
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;
}
}
status = SIXEL_OK;
end:
return status;
}
static void
sixel_debug_print_palette(
sixel_dither_t *dither
)
{
unsigned char *palette;
int i;
palette = sixel_dither_get_palette(dither);
fprintf(stderr, "palette:\n");
for (i = 0; i < sixel_dither_get_num_of_palette_colors(dither); ++i) {
fprintf(stderr, "%d: #%02x%02x%02x\n", i,
palette[i * 3 + 1],
palette[i * 3 + 2],
palette[i * 3 + 3]);
}
}
static SIXELSTATUS
sixel_encoder_output_without_macro(
sixel_frame_t *frame,
sixel_dither_t *dither,
sixel_output_t *output,
sixel_encoder_t *encoder)
{
SIXELSTATUS status = SIXEL_OK;
int dulation = 0;
static unsigned char *p;
int depth;
char message[256];
int nwrite;
#if HAVE_USLEEP
int delay;
useconds_t lag = 0;
# if HAVE_CLOCK
clock_t start;
# endif
#endif
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;
}
#if HAVE_USLEEP && HAVE_CLOCK
start = clock();
#endif
#if HAVE_USLEEP
delay = sixel_frame_get_delay(frame);
if (delay > 0 && !encoder->fignore_delay) {
# if HAVE_CLOCK
dulation = (int)((clock() - start) * 1000 * 1000 / CLOCKS_PER_SEC) - (int)lag;
lag = 0;
# else
dulation = 0;
# endif
if (dulation < 10000 * delay) {
usleep((useconds_t)(10000 * delay - dulation));
} else {
lag = (useconds_t)(10000 * delay - dulation);
}
}
#endif
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 != 0) {
goto end;
}
end:
sixel_allocator_free(encoder->allocator, p);
return status;
}
static SIXELSTATUS
sixel_encoder_output_with_macro(
sixel_frame_t *frame,
sixel_dither_t *dither,
sixel_output_t *output,
sixel_encoder_t *encoder)
{
SIXELSTATUS status = SIXEL_OK;
int dulation = 0;
char buffer[256];
int nwrite;
#if HAVE_USLEEP
useconds_t lag = 0;
# if HAVE_CLOCK
clock_t start;
# endif
#endif
unsigned char *pixbuf;
int width;
int height;
#if HAVE_USLEEP
int delay;
#endif
#if HAVE_USLEEP && HAVE_CLOCK
start = clock();
#endif
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, 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;
}
#if HAVE_USLEEP
delay = sixel_frame_get_delay(frame);
if (delay > 0 && !encoder->fignore_delay) {
# if HAVE_CLOCK
dulation = (int)((clock() - start) * 1000 * 1000 / CLOCKS_PER_SEC) - (int)lag;
lag = 0;
# else
dulation = 0;
# endif
if (dulation < 10000 * delay) {
usleep((useconds_t)(10000 * delay - dulation));
} else {
lag = (useconds_t)(10000 * delay - dulation);
}
}
#endif
}
end:
return status;
}
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;
if (encoder->clipfirst) {
status = sixel_encoder_do_clip(encoder, frame);
if (SIXEL_FAILED(status)) {
goto end;
}
status = sixel_encoder_do_resize(encoder, frame);
if (SIXEL_FAILED(status)) {
goto end;
}
} else {
status = sixel_encoder_do_resize(encoder, frame);
if (SIXEL_FAILED(status)) {
goto end;
}
status = sixel_encoder_do_clip(encoder, frame);
if (SIXEL_FAILED(status)) {
goto end;
}
}
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 (encoder->verbose) {
if (!(sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_GRAYSCALE)) {
sixel_debug_print_palette(dither);
}
}
sixel_dither_set_diffusion_type(dither, encoder->method_for_diffuse);
if (encoder->complexion > 1) {
sixel_dither_set_complexion_score(dither, encoder->complexion);
}
if (output) {
sixel_output_ref(output);
} else {
if (encoder->fuse_macro || encoder->macro_number >= 0) {
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;
}
}
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);
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);
}
if (encoder->cancel_flag && *encoder->cancel_flag) {
status = SIXEL_INTERRUPTED;
goto end;
}
if (encoder->fuse_macro) {
status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
} else if (encoder->macro_number >= 0) {
status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
} else {
status = sixel_encoder_output_without_macro(frame, dither, output, encoder);
}
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;
}
if (SIXEL_FAILED(status)) {
goto end;
}
end:
if (output) {
sixel_output_unref(output);
}
if (dither) {
sixel_dither_unref(dither);
}
return status;
}
SIXELAPI SIXELSTATUS
sixel_encoder_new(
sixel_encoder_t **ppencoder,
sixel_allocator_t *allocator)
{
SIXELSTATUS status = SIXEL_FALSE;
char const *env_default_bgcolor;
char const *env_default_ncolors;
int ncolors;
if (allocator == NULL) {
status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto end;
}
} else {
sixel_allocator_ref(allocator);
}
*ppencoder
= (sixel_encoder_t *)sixel_allocator_malloc(allocator,
sizeof(sixel_encoder_t));
if (*ppencoder == NULL) {
sixel_helper_set_additional_message(
"sixel_encoder_new: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
sixel_allocator_unref(allocator);
goto end;
}
(*ppencoder)->ref = 1;
(*ppencoder)->reqcolors = (-1);
(*ppencoder)->mapfile = NULL;
(*ppencoder)->color_option = SIXEL_COLOR_OPTION_DEFAULT;
(*ppencoder)->builtin_palette = 0;
(*ppencoder)->method_for_diffuse = SIXEL_DIFFUSE_AUTO;
(*ppencoder)->method_for_largest = SIXEL_LARGE_AUTO;
(*ppencoder)->method_for_rep = SIXEL_REP_AUTO;
(*ppencoder)->quality_mode = SIXEL_QUALITY_AUTO;
(*ppencoder)->method_for_resampling = SIXEL_RES_BILINEAR;
(*ppencoder)->loop_mode = SIXEL_LOOP_AUTO;
(*ppencoder)->palette_type = SIXEL_PALETTETYPE_AUTO;
(*ppencoder)->f8bit = 0;
(*ppencoder)->has_gri_arg_limit = 0;
(*ppencoder)->finvert = 0;
(*ppencoder)->fuse_macro = 0;
(*ppencoder)->fignore_delay = 0;
(*ppencoder)->complexion = 1;
(*ppencoder)->fstatic = 0;
(*ppencoder)->pixelwidth = (-1);
(*ppencoder)->pixelheight = (-1);
(*ppencoder)->percentwidth = (-1);
(*ppencoder)->percentheight = (-1);
(*ppencoder)->clipx = 0;
(*ppencoder)->clipy = 0;
(*ppencoder)->clipwidth = 0;
(*ppencoder)->clipheight = 0;
(*ppencoder)->clipfirst = 0;
(*ppencoder)->macro_number = (-1);
(*ppencoder)->verbose = 0;
(*ppencoder)->penetrate_multiplexer = 0;
(*ppencoder)->encode_policy = SIXEL_ENCODEPOLICY_AUTO;
(*ppencoder)->pipe_mode = 0;
(*ppencoder)->bgcolor = NULL;
(*ppencoder)->outfd = STDOUT_FILENO;
(*ppencoder)->finsecure = 0;
(*ppencoder)->cancel_flag = NULL;
(*ppencoder)->dither_cache = NULL;
(*ppencoder)->allocator = allocator;
env_default_bgcolor = getenv("SIXEL_BGCOLOR");
if (env_default_bgcolor) {
status = sixel_parse_x_colorspec(&(*ppencoder)->bgcolor,
env_default_bgcolor,
allocator);
if (SIXEL_FAILED(status)) {
sixel_allocator_free(allocator, *ppencoder);
sixel_allocator_unref(allocator);
*ppencoder = NULL;
goto end;
}
}
env_default_ncolors = getenv("SIXEL_COLORS");
if (env_default_ncolors) {
ncolors = atoi(env_default_ncolors);
if (ncolors > 1 && ncolors <= 256) {
(*ppencoder)->reqcolors = ncolors;
}
}
sixel_allocator_ref(allocator);
status = SIXEL_OK;
end:
return status;
}
SIXELAPI sixel_encoder_t *
sixel_encoder_create(void)
{
SIXELSTATUS status = SIXEL_FALSE;
sixel_encoder_t *encoder = NULL;
status = sixel_encoder_new(&encoder, NULL);
if (SIXEL_FAILED(status)) {
return NULL;
}
return encoder;
}
static void
sixel_encoder_destroy(sixel_encoder_t *encoder)
{
sixel_allocator_t *allocator;
if (encoder) {
allocator = encoder->allocator;
sixel_allocator_free(allocator, encoder->mapfile);
sixel_allocator_free(allocator, encoder->bgcolor);
sixel_dither_unref(encoder->dither_cache);
if (encoder->outfd
&& encoder->outfd != STDOUT_FILENO
&& encoder->outfd != STDERR_FILENO) {
close(encoder->outfd);
}
sixel_allocator_free(allocator, encoder);
sixel_allocator_unref(allocator);
}
}
SIXELAPI void
sixel_encoder_ref(sixel_encoder_t *encoder)
{
++encoder->ref;
}
SIXELAPI void
sixel_encoder_unref(sixel_encoder_t *encoder)
{
if (encoder != NULL && --encoder->ref == 0) {
sixel_encoder_destroy(encoder);
}
}
SIXELAPI SIXELSTATUS
sixel_encoder_set_cancel_flag(
sixel_encoder_t *encoder,
int *cancel_flag
)
{
SIXELSTATUS status = SIXEL_OK;
encoder->cancel_flag = cancel_flag;
return status;
}
SIXELAPI SIXELSTATUS
sixel_encoder_setopt(
sixel_encoder_t *encoder,
int arg,
char const *value)
{
SIXELSTATUS status = SIXEL_FALSE;
int number;
int parsed;
char unit[32];
sixel_encoder_ref(encoder);
switch(arg) {
case SIXEL_OPTFLAG_OUTFILE:
if (*value == '\0') {
sixel_helper_set_additional_message(
"no file name specified.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
if (strcmp(value, "-") != 0) {
if (encoder->outfd && encoder->outfd != STDOUT_FILENO) {
close(encoder->outfd);
}
encoder->outfd = open(value,
O_RDWR|O_CREAT,
S_IREAD|S_IWRITE);
}
break;
case SIXEL_OPTFLAG_7BIT_MODE:
encoder->f8bit = 0;
break;
case SIXEL_OPTFLAG_8BIT_MODE:
encoder->f8bit = 1;
break;
case SIXEL_OPTFLAG_HAS_GRI_ARG_LIMIT:
encoder->has_gri_arg_limit = 1;
break;
case SIXEL_OPTFLAG_COLORS:
encoder->reqcolors = atoi(value);
break;
case SIXEL_OPTFLAG_MAPFILE:
if (encoder->mapfile) {
sixel_allocator_free(encoder->allocator, encoder->mapfile);
}
encoder->mapfile = arg_strdup(value, encoder->allocator);
if (encoder->mapfile == NULL) {
sixel_helper_set_additional_message(
"sixel_encoder_setopt: sixel_allocator_malloc() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
encoder->color_option = SIXEL_COLOR_OPTION_MAPFILE;
break;
case SIXEL_OPTFLAG_MONOCHROME:
encoder->color_option = SIXEL_COLOR_OPTION_MONOCHROME;
break;
case SIXEL_OPTFLAG_HIGH_COLOR:
encoder->color_option = SIXEL_COLOR_OPTION_HIGHCOLOR;
break;
case SIXEL_OPTFLAG_BUILTIN_PALETTE:
if (strcmp(value, "xterm16") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_XTERM16;
} else if (strcmp(value, "xterm256") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_XTERM256;
} else if (strcmp(value, "vt340mono") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_VT340_MONO;
} else if (strcmp(value, "vt340color") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_VT340_COLOR;
} else if (strcmp(value, "gray1") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_G1;
} else if (strcmp(value, "gray2") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_G2;
} else if (strcmp(value, "gray4") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_G4;
} else if (strcmp(value, "gray8") == 0) {
encoder->builtin_palette = SIXEL_BUILTIN_G8;
} else {
sixel_helper_set_additional_message(
"cannot parse builtin palette option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
encoder->color_option = SIXEL_COLOR_OPTION_BUILTIN;
break;
case SIXEL_OPTFLAG_DIFFUSION:
if (strcmp(value, "auto") == 0) {
encoder->method_for_diffuse = SIXEL_DIFFUSE_AUTO;
} else if (strcmp(value, "none") == 0) {
encoder->method_for_diffuse = SIXEL_DIFFUSE_NONE;
} else if (strcmp(value, "fs") == 0) {
encoder->method_for_diffuse = SIXEL_DIFFUSE_FS;
} else if (strcmp(value, "atkinson") == 0) {
encoder->method_for_diffuse = SIXEL_DIFFUSE_ATKINSON;
} else if (strcmp(value, "jajuni") == 0) {
encoder->method_for_diffuse = SIXEL_DIFFUSE_JAJUNI;
} else if (strcmp(value, "stucki") == 0) {
encoder->method_for_diffuse = SIXEL_DIFFUSE_STUCKI;
} else if (strcmp(value, "burkes") == 0) {
encoder->method_for_diffuse = SIXEL_DIFFUSE_BURKES;
} else {
sixel_helper_set_additional_message(
"specified diffusion method is not supported.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_FIND_LARGEST:
if (value) {
if (strcmp(value, "auto") == 0) {
encoder->method_for_largest = SIXEL_LARGE_AUTO;
} else if (strcmp(value, "norm") == 0) {
encoder->method_for_largest = SIXEL_LARGE_NORM;
} else if (strcmp(value, "lum") == 0) {
encoder->method_for_largest = SIXEL_LARGE_LUM;
} else {
sixel_helper_set_additional_message(
"specified finding method is not supported.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
}
break;
case SIXEL_OPTFLAG_SELECT_COLOR:
if (strcmp(value, "auto") == 0) {
encoder->method_for_rep = SIXEL_REP_AUTO;
} else if (strcmp(value, "center") == 0) {
encoder->method_for_rep = SIXEL_REP_CENTER_BOX;
} else if (strcmp(value, "average") == 0) {
encoder->method_for_rep = SIXEL_REP_AVERAGE_COLORS;
} else if ((strcmp(value, "histogram") == 0) ||
(strcmp(value, "histgram") == 0)) {
encoder->method_for_rep = SIXEL_REP_AVERAGE_PIXELS;
} else {
sixel_helper_set_additional_message(
"specified finding method is not supported.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_CROP:
number = sscanf(value, "%dx%d+%d+%d",
&encoder->clipwidth, &encoder->clipheight,
&encoder->clipx, &encoder->clipy);
if (number != 4) {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
if (encoder->clipwidth <= 0 || encoder->clipheight <= 0) {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
if (encoder->clipx < 0 || encoder->clipy < 0) {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
encoder->clipfirst = 0;
break;
case SIXEL_OPTFLAG_WIDTH:
parsed = sscanf(value, "%d%2s", &number, unit);
if (parsed == 2 && strcmp(unit, "%") == 0) {
encoder->pixelwidth = (-1);
encoder->percentwidth = number;
} else if (parsed == 1 || (parsed == 2 && strcmp(unit, "px") == 0)) {
encoder->pixelwidth = number;
encoder->percentwidth = (-1);
} else if (strcmp(value, "auto") == 0) {
encoder->pixelwidth = (-1);
encoder->percentwidth = (-1);
} else {
sixel_helper_set_additional_message(
"cannot parse -w/--width option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
if (encoder->clipwidth) {
encoder->clipfirst = 1;
}
break;
case SIXEL_OPTFLAG_HEIGHT:
parsed = sscanf(value, "%d%2s", &number, unit);
if (parsed == 2 && strcmp(unit, "%") == 0) {
encoder->pixelheight = (-1);
encoder->percentheight = number;
} else if (parsed == 1 || (parsed == 2 && strcmp(unit, "px") == 0)) {
encoder->pixelheight = number;
encoder->percentheight = (-1);
} else if (strcmp(value, "auto") == 0) {
encoder->pixelheight = (-1);
encoder->percentheight = (-1);
} else {
sixel_helper_set_additional_message(
"cannot parse -h/--height option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
if (encoder->clipheight) {
encoder->clipfirst = 1;
}
break;
case SIXEL_OPTFLAG_RESAMPLING:
if (strcmp(value, "nearest") == 0) {
encoder->method_for_resampling = SIXEL_RES_NEAREST;
} else if (strcmp(value, "gaussian") == 0) {
encoder->method_for_resampling = SIXEL_RES_GAUSSIAN;
} else if (strcmp(value, "hanning") == 0) {
encoder->method_for_resampling = SIXEL_RES_HANNING;
} else if (strcmp(value, "hamming") == 0) {
encoder->method_for_resampling = SIXEL_RES_HAMMING;
} else if (strcmp(value, "bilinear") == 0) {
encoder->method_for_resampling = SIXEL_RES_BILINEAR;
} else if (strcmp(value, "welsh") == 0) {
encoder->method_for_resampling = SIXEL_RES_WELSH;
} else if (strcmp(value, "bicubic") == 0) {
encoder->method_for_resampling = SIXEL_RES_BICUBIC;
} else if (strcmp(value, "lanczos2") == 0) {
encoder->method_for_resampling = SIXEL_RES_LANCZOS2;
} else if (strcmp(value, "lanczos3") == 0) {
encoder->method_for_resampling = SIXEL_RES_LANCZOS3;
} else if (strcmp(value, "lanczos4") == 0) {
encoder->method_for_resampling = SIXEL_RES_LANCZOS4;
} else {
sixel_helper_set_additional_message(
"specified desampling method is not supported.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_QUALITY:
if (strcmp(value, "auto") == 0) {
encoder->quality_mode = SIXEL_QUALITY_AUTO;
} else if (strcmp(value, "high") == 0) {
encoder->quality_mode = SIXEL_QUALITY_HIGH;
} else if (strcmp(value, "low") == 0) {
encoder->quality_mode = SIXEL_QUALITY_LOW;
} else if (strcmp(value, "full") == 0) {
encoder->quality_mode = SIXEL_QUALITY_FULL;
} else {
sixel_helper_set_additional_message(
"cannot parse quality option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_LOOPMODE:
if (strcmp(value, "auto") == 0) {
encoder->loop_mode = SIXEL_LOOP_AUTO;
} else if (strcmp(value, "force") == 0) {
encoder->loop_mode = SIXEL_LOOP_FORCE;
} else if (strcmp(value, "disable") == 0) {
encoder->loop_mode = SIXEL_LOOP_DISABLE;
} else {
sixel_helper_set_additional_message(
"cannot parse loop-control option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_PALETTE_TYPE:
if (strcmp(value, "auto") == 0) {
encoder->palette_type = SIXEL_PALETTETYPE_AUTO;
} else if (strcmp(value, "hls") == 0) {
encoder->palette_type = SIXEL_PALETTETYPE_HLS;
} else if (strcmp(value, "rgb") == 0) {
encoder->palette_type = SIXEL_PALETTETYPE_RGB;
} else {
sixel_helper_set_additional_message(
"cannot parse palette type option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_BGCOLOR:
if (encoder->bgcolor) {
sixel_allocator_free(encoder->allocator, encoder->bgcolor);
}
status = sixel_parse_x_colorspec(&encoder->bgcolor,
value,
encoder->allocator);
if (SIXEL_FAILED(status)) {
sixel_helper_set_additional_message(
"cannot parse bgcolor option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_INSECURE:
encoder->finsecure = 1;
break;
case SIXEL_OPTFLAG_INVERT:
encoder->finvert = 1;
break;
case SIXEL_OPTFLAG_USE_MACRO:
encoder->fuse_macro = 1;
break;
case SIXEL_OPTFLAG_MACRO_NUMBER:
encoder->macro_number = atoi(value);
if (encoder->macro_number < 0) {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_IGNORE_DELAY:
encoder->fignore_delay = 1;
break;
case SIXEL_OPTFLAG_VERBOSE:
encoder->verbose = 1;
break;
case SIXEL_OPTFLAG_STATIC:
encoder->fstatic = 1;
break;
case SIXEL_OPTFLAG_PENETRATE:
encoder->penetrate_multiplexer = 1;
break;
case SIXEL_OPTFLAG_ENCODE_POLICY:
if (strcmp(value, "auto") == 0) {
encoder->encode_policy = SIXEL_ENCODEPOLICY_AUTO;
} else if (strcmp(value, "fast") == 0) {
encoder->encode_policy = SIXEL_ENCODEPOLICY_FAST;
} else if (strcmp(value, "size") == 0) {
encoder->encode_policy = SIXEL_ENCODEPOLICY_SIZE;
} else {
sixel_helper_set_additional_message(
"cannot parse encode policy option.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_COMPLEXION_SCORE:
encoder->complexion = atoi(value);
if (encoder->complexion < 1) {
sixel_helper_set_additional_message(
"complexion parameter must be 1 or more.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
break;
case SIXEL_OPTFLAG_PIPE_MODE:
encoder->pipe_mode = 1;
break;
case '?':
default:
sixel_helper_set_additional_message(
"unknwon option is specified.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
if (encoder->reqcolors != (-1)) {
switch (encoder->color_option) {
case SIXEL_COLOR_OPTION_MAPFILE:
sixel_helper_set_additional_message(
"option -p, --colors conflicts with -m, --mapfile.");
status = SIXEL_BAD_ARGUMENT;
goto end;
case SIXEL_COLOR_OPTION_MONOCHROME:
sixel_helper_set_additional_message(
"option -e, --monochrome conflicts with -p, --colors.");
status = SIXEL_BAD_ARGUMENT;
goto end;
case SIXEL_COLOR_OPTION_HIGHCOLOR:
sixel_helper_set_additional_message(
"option -p, --colors conflicts with -I, --high-color.");
status = SIXEL_BAD_ARGUMENT;
goto end;
case SIXEL_COLOR_OPTION_BUILTIN:
sixel_helper_set_additional_message(
"option -p, --colors conflicts with -b, --builtin-palette.");
status = SIXEL_BAD_ARGUMENT;
goto end;
default:
break;
}
}
if (encoder->f8bit && encoder->penetrate_multiplexer) {
sixel_helper_set_additional_message(
"option -8 --8bit-mode conflicts"
" with -P, --penetrate.");
status = SIXEL_BAD_ARGUMENT;
goto end;
}
status = SIXEL_OK;
end:
sixel_encoder_unref(encoder);
return status;
}
static SIXELSTATUS
load_image_callback(sixel_frame_t *frame, void *data)
{
return sixel_encoder_encode_frame((sixel_encoder_t *)data, frame, NULL);
}
SIXELAPI SIXELSTATUS
sixel_encoder_encode(
sixel_encoder_t *encoder,
char const *filename)
{
SIXELSTATUS status = SIXEL_FALSE;
int fuse_palette = 1;
if (encoder == NULL) {
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
encoder = sixel_encoder_create();
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic pop
#endif
if (encoder == NULL) {
sixel_helper_set_additional_message(
"sixel_encoder_encode: sixel_encoder_create() failed.");
status = SIXEL_BAD_ALLOCATION;
goto end;
}
} else {
sixel_encoder_ref(encoder);
}
if (encoder->reqcolors == (-1)) {
encoder->reqcolors = SIXEL_PALETTE_MAX;
}
if (encoder->reqcolors < 2) {
encoder->reqcolors = SIXEL_PALETTE_MIN;
}
if (encoder->palette_type == SIXEL_PALETTETYPE_AUTO) {
encoder->palette_type = SIXEL_PALETTETYPE_RGB;
}
if (encoder->color_option != SIXEL_COLOR_OPTION_DEFAULT) {
fuse_palette = 0;
}
if (encoder->percentwidth > 0 ||
encoder->percentheight > 0 ||
encoder->pixelwidth > 0 ||
encoder->pixelheight > 0) {
fuse_palette = 0;
}
reload:
status = sixel_helper_load_image_file(filename,
encoder->fstatic,
fuse_palette,
encoder->reqcolors,
encoder->bgcolor,
encoder->loop_mode,
load_image_callback,
encoder->finsecure,
encoder->cancel_flag,
(void *)encoder,
encoder->allocator);
if (status != SIXEL_OK) {
goto end;
}
if (encoder->pipe_mode) {
#if HAVE_CLEARERR
clearerr(stdin);
#endif
while (encoder->cancel_flag && !*encoder->cancel_flag) {
status = sixel_tty_wait_stdin(1000000);
if (SIXEL_FAILED(status)) {
goto end;
}
if (status != SIXEL_OK) {
break;
}
}
if (!encoder->cancel_flag || !*encoder->cancel_flag) {
goto reload;
}
}
end:
sixel_encoder_unref(encoder);
return status;
}
SIXELAPI SIXELSTATUS
sixel_encoder_encode_bytes(
sixel_encoder_t *encoder,
unsigned char *bytes,
int width,
int height,
int pixelformat,
unsigned char *palette,
int ncolors)
{
SIXELSTATUS status = SIXEL_FALSE;
sixel_frame_t *frame;
if (encoder == NULL || bytes == NULL) {
status = SIXEL_BAD_ARGUMENT;
goto end;
}
status = sixel_frame_new(&frame, encoder->allocator);
if (SIXEL_FAILED(status)) {
goto end;
}
status = sixel_frame_init(frame, bytes, width, height,
pixelformat, palette, ncolors);
if (SIXEL_FAILED(status)) {
goto end;
}
status = sixel_encoder_encode_frame(encoder, frame, NULL);
if (SIXEL_FAILED(status)) {
goto end;
}
status = SIXEL_OK;
end:
return status;
}
#if HAVE_TESTS
static int
test1(void)
{
int nret = EXIT_FAILURE;
sixel_encoder_t *encoder = NULL;
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
encoder = sixel_encoder_create();
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic pop
#endif
if (encoder == NULL) {
goto error;
}
sixel_encoder_ref(encoder);
sixel_encoder_unref(encoder);
nret = EXIT_SUCCESS;
error:
sixel_encoder_unref(encoder);
return nret;
}
static int
test2(void)
{
int nret = EXIT_FAILURE;
SIXELSTATUS status;
sixel_encoder_t *encoder = NULL;
sixel_frame_t *frame = NULL;
unsigned char *buffer;
int height = 0;
int is_animation = 0;
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
encoder = sixel_encoder_create();
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic pop
#endif
if (encoder == NULL) {
goto error;
}
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
frame = sixel_frame_create();
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic pop
#endif
if (encoder == NULL) {
goto error;
}
buffer = (unsigned char *)sixel_allocator_malloc(encoder->allocator, 3);
if (buffer == NULL) {
goto error;
}
status = sixel_frame_init(frame, buffer, 1, 1,
SIXEL_PIXELFORMAT_RGB888,
NULL, 0);
if (SIXEL_FAILED(status)) {
goto error;
}
if (sixel_frame_get_loop_no(frame) != 0 || sixel_frame_get_frame_no(frame) != 0) {
is_animation = 1;
}
height = sixel_frame_get_height(frame);
status = sixel_tty_scroll(sixel_write_callback, encoder->outfd, height, is_animation);
if (SIXEL_FAILED(status)) {
goto error;
}
nret = EXIT_SUCCESS;
error:
sixel_encoder_unref(encoder);
sixel_frame_unref(frame);
return nret;
}
static int
test3(void)
{
int nret = EXIT_FAILURE;
int result;
result = sixel_tty_wait_stdin(1000);
if (result != 0) {
goto error;
}
nret = EXIT_SUCCESS;
error:
return nret;
}
static int
test4(void)
{
int nret = EXIT_FAILURE;
sixel_encoder_t *encoder = NULL;
SIXELSTATUS status;
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
encoder = sixel_encoder_create();
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
# pragma GCC diagnostic pop
#endif
if (encoder == NULL) {
goto error;
}
status = sixel_encoder_setopt(encoder,
SIXEL_OPTFLAG_LOOPMODE,
"force");
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_encoder_setopt(encoder,
SIXEL_OPTFLAG_PIPE_MODE,
"force");
if (SIXEL_FAILED(status)) {
goto error;
}
nret = EXIT_SUCCESS;
error:
sixel_encoder_unref(encoder);
return nret;
}
static int
test5(void)
{
int nret = EXIT_FAILURE;
sixel_encoder_t *encoder = NULL;
sixel_allocator_t *allocator = NULL;
SIXELSTATUS status;
status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
if (SIXEL_FAILED(status)) {
goto error;
}
status = sixel_encoder_new(&encoder, allocator);
if (SIXEL_FAILED(status)) {
goto error;
}
sixel_encoder_ref(encoder);
sixel_encoder_unref(encoder);
nret = EXIT_SUCCESS;
error:
sixel_encoder_unref(encoder);
return nret;
}
int
sixel_encoder_tests_main(void)
{
int nret = EXIT_FAILURE;
size_t i;
typedef int (* testcase)(void);
static testcase const testcases[] = {
test1,
test2,
test3,
test4,
test5
};
for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
nret = testcases[i]();
if (nret != EXIT_SUCCESS) {
goto error;
}
}
nret = EXIT_SUCCESS;
error:
return nret;
}
#endif