• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

saitoha / libsixel / 18635717249

19 Oct 2025 08:30PM UTC coverage: 54.456% (-0.1%) from 54.584%
18635717249

push

github

saitoha
completion: update bash/zsh completion files

5216 of 14275 branches covered (36.54%)

7167 of 13161 relevant lines covered (54.46%)

1133430.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.05
/src/encoder.c
1
/* SPDX-License-Identifier: MIT AND BSD-3-Clause
2
 *
3
 * Copyright (c) 2014-2019 Hayaki Saito
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6
 * this software and associated documentation files (the "Software"), to deal in
7
 * the Software without restriction, including without limitation the rights to
8
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
 * the Software, and to permit persons to whom the Software is furnished to do so,
10
 * subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be included in all
13
 * copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
 *
22
 * -------------------------------------------------------------------------------
23
 * Portions of this file(sixel_encoder_emit_drcsmmv2_chars) are derived from
24
 * mlterm's drcssixel.c.
25
 *
26
 * Copyright (c) Araki Ken(arakiken@users.sourceforge.net)
27
 *
28
 * Redistribution and use in source and binary forms, with or without
29
 * modification, are permitted provided that the following conditions
30
 * are met:
31
 * 1. Redistributions of source code must retain the above copyright
32
 *    notice, this list of conditions and the following disclaimer.
33
 * 2. Redistributions in binary form must reproduce the above copyright
34
 *    notice, this list of conditions and the following disclaimer in the
35
 *    documentation and/or other materials provided with the distribution.
36
 * 3. The name of any author may not be used to endorse or promote
37
 *    products derived from this software without their specific prior
38
 *    written permission.
39
 *
40
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
41
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
44
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50
 * SUCH DAMAGE.
51
 *
52
 */
53

54
#include "config.h"
55

56
/* STDC_HEADERS */
57
#include <stdio.h>
58
#include <stdlib.h>
59
#include <stdarg.h>
60

61
# if HAVE_STRING_H
62
#include <string.h>
63
#endif  /* HAVE_STRING_H */
64
#if HAVE_UNISTD_H
65
# include <unistd.h>
66
#elif HAVE_SYS_UNISTD_H
67
# include <sys/unistd.h>
68
#endif  /* HAVE_SYS_UNISTD_H */
69
#if HAVE_SYS_TYPES_H
70
# include <sys/types.h>
71
#endif  /* HAVE_SYS_TYPES_H */
72
#if HAVE_INTTYPES_H
73
# include <inttypes.h>
74
#endif  /* HAVE_INTTYPES_H */
75
#if HAVE_ERRNO_H
76
# include <errno.h>
77
#endif  /* HAVE_ERRNO_H */
78
#if HAVE_SYS_STAT_H
79
# include <sys/stat.h>
80
#endif  /* HAVE_SYS_STAT_H */
81
#if HAVE_SYS_IOCTL_H
82
# include <sys/ioctl.h>
83
#endif  /* HAVE_SYS_IOCTL_H */
84
#if HAVE_FCNTL_H
85
# include <fcntl.h>
86
#endif  /* HAVE_FCNTL_H */
87
#if HAVE_ERRNO_H
88
# include <errno.h>
89
#endif  /* HAVE_ERRNO_H */
90
#if HAVE_CTYPE_H
91
# include <ctype.h>
92
#endif  /* HAVE_CTYPE_H */
93

94
#include <sixel.h>
95
#include "loader.h"
96
#include "tty.h"
97
#include "encoder.h"
98
#include "output.h"
99
#include "dither.h"
100
#include "frame.h"
101
#include "rgblookup.h"
102

103
#if defined(_WIN32)
104

105
# include <windows.h>
106
# if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
107
#  include <io.h>
108
# endif
109
# if defined(_MSC_VER)
110
#   include <time.h>
111
# endif
112

113
/* for msvc */
114
# ifndef STDIN_FILENO
115
#  define STDIN_FILENO 0
116
# endif
117
# ifndef STDOUT_FILENO
118
#  define STDOUT_FILENO 1
119
# endif
120
# ifndef STDERR_FILENO
121
#  define STDERR_FILENO 2
122
# endif
123
# ifndef S_IRUSR
124
#  define S_IRUSR _S_IREAD
125
# endif
126
# ifndef S_IWUSR
127
#  define S_IWUSR _S_IWRITE
128
# endif
129

130
# if defined(CLOCKS_PER_SEC)
131
#  undef CLOCKS_PER_SEC
132
# endif
133
# define CLOCKS_PER_SEC 1000
134

135
# if !defined(HAVE_NANOSLEEP)
136
# define HAVE_NANOSLEEP_WIN 1
137
static int
138
nanosleep_win(
139
    struct timespec const *req,
140
    struct timespec *rem)
141
{
142
    LONGLONG nanoseconds;
143
    LARGE_INTEGER dueTime;
144
    HANDLE timer;
145

146
    if (req == NULL || req->tv_sec < 0 || req->tv_nsec < 0 ||
147
        req->tv_nsec >= 1000000000L) {
148
        errno = EINVAL;
149
        return (-1);
150
    }
151

152
    /* Convert to 100-nanosecond intervals (Windows FILETIME units) */
153
    nanoseconds = req->tv_sec * 1000000000LL + req->tv_nsec;
154
    dueTime.QuadPart = -(nanoseconds / 100); /* Negative for relative time */
155

156
    timer = CreateWaitableTimer(NULL, TRUE, NULL);
157
    if (timer == NULL) {
158
        errno = EFAULT;  /* Approximate error */
159
        return (-1);
160
    }
161

162
    if (! SetWaitableTimer(timer, &dueTime, 0, NULL, NULL, FALSE)) {
163
        (void) CloseHandle(timer);
164
        errno = EFAULT;
165
        return (-1);
166
    }
167

168
    (void) WaitForSingleObject(timer, INFINITE);
169
    (void) CloseHandle(timer);
170

171
    /* No interruption handling, so rem is unchanged */
172
    if (rem != NULL) {
173
        rem->tv_sec = 0;
174
        rem->tv_nsec = 0;
175
    }
176

177
    return (0);
178
}
179
# endif  /* HAVE_NANOSLEEP */
180

181
# if !defined(HAVE_CLOCK)
182
# define HAVE_CLOCK_WIN 1
183
static sixel_clock_t
184
clock_win(void)
185
{
186
    FILETIME ct, et, kt, ut;
187
    ULARGE_INTEGER u, k;
188

189
    if (! GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
190
        return (sixel_clock_t)(-1);
191
    }
192
    u.LowPart = ut.dwLowDateTime; u.HighPart = ut.dwHighDateTime;
193
    k.LowPart = kt.dwLowDateTime; k.HighPart = kt.dwHighDateTime;
194
    /* 100ns -> ms */
195
    return (sixel_clock_t)((u.QuadPart + k.QuadPart) / 10000ULL);
196
}
197
# endif  /* HAVE_CLOCK */
198

199
#endif /* _WIN32 */
200

201

202
static char *
203
arg_strdup(
60✔
204
    char const          /* in */ *s,          /* source buffer */
205
    sixel_allocator_t   /* in */ *allocator)  /* allocator object for
206
                                                 destination buffer */
207
{
208
    char *p;
209
    size_t len;
210

211
    len = strlen(s);
60✔
212

213
    p = (char *)sixel_allocator_malloc(allocator, len + 1);
60✔
214
    if (p) {
60!
215
#if HAVE_STRCPY_S
216
        (void) strcpy_s(p, (rsize_t)len, s);
217
#else
218
        (void) strcpy(p, s);
60✔
219
#endif  /* HAVE_STRCPY_S */
220
    }
20✔
221
    return p;
60✔
222
}
223

224

225
/* An clone function of XColorSpec() of xlib */
226
static SIXELSTATUS
227
sixel_parse_x_colorspec(
45✔
228
    unsigned char       /* out */ **bgcolor,     /* destination buffer */
229
    char const          /* in */  *s,            /* source buffer */
230
    sixel_allocator_t   /* in */  *allocator)    /* allocator object for
231
                                                    destination buffer */
232
{
233
    SIXELSTATUS status = SIXEL_FALSE;
45✔
234
    char *p;
235
    unsigned char components[3];
236
    int component_index = 0;
45✔
237
    unsigned long v;
238
    char *endptr;
239
    char *buf = NULL;
45✔
240
    struct color const *pcolor;
241

242
    /* from rgb_lookup.h generated by gpref */
243
    pcolor = lookup_rgb(s, strlen(s));
45✔
244
    if (pcolor) {
45✔
245
        *bgcolor = (unsigned char *)sixel_allocator_malloc(allocator, 3);
3✔
246
        if (*bgcolor == NULL) {
3!
247
            sixel_helper_set_additional_message(
×
248
                "sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
249
            status = SIXEL_BAD_ALLOCATION;
×
250
            goto end;
×
251
        }
252
        (*bgcolor)[0] = pcolor->r;
3✔
253
        (*bgcolor)[1] = pcolor->g;
3✔
254
        (*bgcolor)[2] = pcolor->b;
3✔
255
    } else if (s[0] == 'r' && s[1] == 'g' && s[2] == 'b' && s[3] == ':') {
43!
256
        p = buf = arg_strdup(s + 4, allocator);
6✔
257
        if (buf == NULL) {
6!
258
            sixel_helper_set_additional_message(
×
259
                "sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
260
            status = SIXEL_BAD_ALLOCATION;
×
261
            goto end;
×
262
        }
263
        while (*p) {
15!
264
            v = 0;
15✔
265
            for (endptr = p; endptr - p <= 12; ++endptr) {
36!
266
                if (*endptr >= '0' && *endptr <= '9') {
36✔
267
                    v = (v << 4) | (unsigned long)(*endptr - '0');
15✔
268
                } else if (*endptr >= 'a' && *endptr <= 'f') {
26!
269
                    v = (v << 4) | (unsigned long)(*endptr - 'a' + 10);
3✔
270
                } else if (*endptr >= 'A' && *endptr <= 'F') {
19!
271
                    v = (v << 4) | (unsigned long)(*endptr - 'A' + 10);
3✔
272
                } else {
1✔
273
                    break;
5✔
274
                }
275
            }
7✔
276
            if (endptr - p == 0) {
15!
277
                break;
×
278
            }
279
            if (endptr - p > 4) {
15!
280
                break;
×
281
            }
282
            v = v << ((4 - (endptr - p)) * 4) >> 8;
15✔
283
            components[component_index++] = (unsigned char)v;
15✔
284
            p = endptr;
15✔
285
            if (component_index == 3) {
15✔
286
                break;
3✔
287
            }
288
            if (*p == '\0') {
12✔
289
                break;
3✔
290
            }
291
            if (*p != '/') {
9!
292
                break;
×
293
            }
294
            ++p;
9✔
295
        }
296
        if (component_index != 3 || *p != '\0' || *p == '/') {
6!
297
            status = SIXEL_BAD_ARGUMENT;
3✔
298
            goto end;
3✔
299
        }
300
        *bgcolor = (unsigned char *)sixel_allocator_malloc(allocator, 3);
3✔
301
        if (*bgcolor == NULL) {
3!
302
            sixel_helper_set_additional_message(
×
303
                "sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
304
            status = SIXEL_BAD_ALLOCATION;
×
305
            goto end;
×
306
        }
307
        (*bgcolor)[0] = components[0];
3✔
308
        (*bgcolor)[1] = components[1];
3✔
309
        (*bgcolor)[2] = components[2];
3✔
310
    } else if (*s == '#') {
37✔
311
        buf = arg_strdup(s + 1, allocator);
27✔
312
        if (buf == NULL) {
27!
313
            sixel_helper_set_additional_message(
×
314
                "sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
315
            status = SIXEL_BAD_ALLOCATION;
×
316
            goto end;
×
317
        }
318
        for (p = endptr = buf; endptr - p <= 12; ++endptr) {
192✔
319
            if (*endptr >= '0' && *endptr <= '9') {
189✔
320
                *endptr -= '0';
99✔
321
            } else if (*endptr >= 'a' && *endptr <= 'f') {
123!
322
                *endptr -= 'a' - 10;
57✔
323
            } else if (*endptr >= 'A' && *endptr <= 'F') {
52✔
324
                *endptr -= 'A' - 10;
9✔
325
            } else if (*endptr == '\0') {
27✔
326
                break;
21✔
327
            } else {
328
                status = SIXEL_BAD_ARGUMENT;
3✔
329
                goto end;
3✔
330
            }
331
        }
55✔
332
        if (endptr - p > 12) {
24✔
333
            status = SIXEL_BAD_ARGUMENT;
3✔
334
            goto end;
3✔
335
        }
336
        *bgcolor = (unsigned char *)sixel_allocator_malloc(allocator, 3);
21✔
337
        if (*bgcolor == NULL) {
21!
338
            sixel_helper_set_additional_message(
×
339
                "sixel_parse_x_colorspec: sixel_allocator_malloc() failed.");
340
            status = SIXEL_BAD_ALLOCATION;
×
341
            goto end;
×
342
        }
343
        switch (endptr - p) {
21✔
344
        case 3:
6✔
345
            (*bgcolor)[0] = (unsigned char)(p[0] << 4);
9✔
346
            (*bgcolor)[1] = (unsigned char)(p[1] << 4);
9✔
347
            (*bgcolor)[2] = (unsigned char)(p[2] << 4);
9✔
348
            break;
9✔
349
        case 6:
2✔
350
            (*bgcolor)[0] = (unsigned char)(p[0] << 4 | p[1]);
3✔
351
            (*bgcolor)[1] = (unsigned char)(p[2] << 4 | p[3]);
3✔
352
            (*bgcolor)[2] = (unsigned char)(p[4] << 4 | p[4]);
3✔
353
            break;
3✔
354
        case 9:
2✔
355
            (*bgcolor)[0] = (unsigned char)(p[0] << 4 | p[1]);
3✔
356
            (*bgcolor)[1] = (unsigned char)(p[3] << 4 | p[4]);
3✔
357
            (*bgcolor)[2] = (unsigned char)(p[6] << 4 | p[7]);
3✔
358
            break;
3✔
359
        case 12:
2✔
360
            (*bgcolor)[0] = (unsigned char)(p[0] << 4 | p[1]);
3✔
361
            (*bgcolor)[1] = (unsigned char)(p[4] << 4 | p[5]);
3✔
362
            (*bgcolor)[2] = (unsigned char)(p[8] << 4 | p[9]);
3✔
363
            break;
3✔
364
        default:
2✔
365
            status = SIXEL_BAD_ARGUMENT;
3✔
366
            goto end;
3✔
367
        }
368
    } else {
6✔
369
        status = SIXEL_BAD_ARGUMENT;
9✔
370
        goto end;
9✔
371
    }
372

373
    status = SIXEL_OK;
24✔
374

375
end:
30✔
376
    sixel_allocator_free(allocator, buf);
45✔
377

378
    return status;
45✔
379
}
380

381

382
/* generic writer function for passing to sixel_output_new() */
383
static int
384
sixel_write_callback(char *data, int size, void *priv)
3,958✔
385
{
386
    int result;
387

388
#if HAVE__WRITE
389
    result = _write(*(int *)priv, data, (size_t)size);
390
#elif defined(__MINGW64__)
391
    result = write(*(int *)priv, data, (unsigned int)size);
392
#else
393
    result = write(*(int *)priv, data, (size_t)size);
3,958✔
394
#endif
395

396
    return result;
3,958✔
397
}
398

399

400
/* the writer function with hex-encoding for passing to sixel_output_new() */
401
static int
402
sixel_hex_write_callback(
73✔
403
    char    /* in */ *data,
404
    int     /* in */ size,
405
    void    /* in */ *priv)
406
{
407
    char hex[SIXEL_OUTPUT_PACKET_SIZE * 2];
408
    int i;
409
    int j;
410
    int result;
411

412
    for (i = j = 0; i < size; ++i, ++j) {
635,629✔
413
        hex[j] = (data[i] >> 4) & 0xf;
635,556✔
414
        hex[j] += (hex[j] < 10 ? '0': ('a' - 10));
635,556!
415
        hex[++j] = data[i] & 0xf;
635,556✔
416
        hex[j] += (hex[j] < 10 ? '0': ('a' - 10));
635,556✔
417
    }
168,040✔
418

419
#if HAVE__WRITE
420
    result = _write(*(int *)priv, hex, (unsigned int)(size * 2));
421
#elif defined(__MINGW64__)
422
    result = write(*(int *)priv, hex, (unsigned int)(size * 2));
423
#else
424
    result = write(*(int *)priv, hex, (size_t)(size * 2));
73✔
425
#endif
426

427
    return result;
73✔
428
}
429

430
static SIXELSTATUS
431
sixel_encoder_ensure_cell_size(sixel_encoder_t *encoder)
×
432
{
433
#if defined(TIOCGWINSZ)
434
    struct winsize ws;
435
    int result;
436
    int fd = 0;
×
437

438
    if (encoder->cell_width > 0 && encoder->cell_height > 0) {
×
439
        return SIXEL_OK;
×
440
    }
441

442
#if HAVE__OPEN
443
    fd = _open("/dev/tty", O_RDONLY);
444
#else
445
    fd = open("/dev/tty", O_RDONLY);
×
446
#endif  /* #if HAVE__OPEN */
447
    if (fd >= 0) {
×
448
        result = ioctl(fd, TIOCGWINSZ, &ws);
×
449
        close(fd);
×
450
    } else {
451
        sixel_helper_set_additional_message(
×
452
            "failed to open /dev/tty");
453
        return (SIXEL_LIBC_ERROR | (errno & 0xff));
×
454
    }
455
    if (result != 0) {
×
456
        sixel_helper_set_additional_message(
×
457
            "failed to query terminal geometry with ioctl().");
458
        return (SIXEL_LIBC_ERROR | (errno & 0xff));
×
459
    }
460

461
    if (ws.ws_col <= 0 || ws.ws_row <= 0 ||
×
462
        ws.ws_xpixel <= ws.ws_col || ws.ws_ypixel <= ws.ws_row) {
×
463
        sixel_helper_set_additional_message(
×
464
            "terminal does not report pixel cell size for drcs option.");
465
        return SIXEL_BAD_ARGUMENT;
×
466
    }
467

468
    encoder->cell_width = ws.ws_xpixel / ws.ws_col;
×
469
    encoder->cell_height = ws.ws_ypixel / ws.ws_row;
×
470
    if (encoder->cell_width <= 0 || encoder->cell_height <= 0) {
×
471
        sixel_helper_set_additional_message(
×
472
            "terminal cell size reported zero via ioctl().");
473
        return SIXEL_BAD_ARGUMENT;
×
474
    }
475

476
    return SIXEL_OK;
×
477
#else
478
    (void) encoder;
479
    sixel_helper_set_additional_message(
480
        "drcs option is not supported on this platform.");
481
    return SIXEL_NOT_IMPLEMENTED;
482
#endif
483
}
484

485

486
/* returns monochrome dithering context object */
487
static SIXELSTATUS
488
sixel_prepare_monochrome_palette(
12✔
489
    sixel_dither_t  /* out */ **dither,
490
     int            /* in */  finvert)
491
{
492
    SIXELSTATUS status = SIXEL_FALSE;
12✔
493

494
    if (finvert) {
12✔
495
        *dither = sixel_dither_get(SIXEL_BUILTIN_MONO_LIGHT);
3✔
496
    } else {
1✔
497
        *dither = sixel_dither_get(SIXEL_BUILTIN_MONO_DARK);
9✔
498
    }
499
    if (*dither == NULL) {
12!
500
        sixel_helper_set_additional_message(
×
501
            "sixel_prepare_monochrome_palette: sixel_dither_get() failed.");
502
        status = SIXEL_RUNTIME_ERROR;
×
503
        goto end;
×
504
    }
505

506
    status = SIXEL_OK;
12✔
507

508
end:
8✔
509
    return status;
12✔
510
}
511

512

513
/* returns dithering context object with specified builtin palette */
514
typedef struct palette_conversion {
515
    unsigned char *original;
516
    unsigned char *copy;
517
    size_t size;
518
    int convert_inplace;
519
    int converted;
520
    int frame_colorspace;
521
} palette_conversion_t;
522

523
static SIXELSTATUS
524
sixel_encoder_convert_palette(sixel_encoder_t *encoder,
498✔
525
                              sixel_output_t *output,
526
                              sixel_dither_t *dither,
527
                              int frame_colorspace,
528
                              int pixelformat,
529
                              palette_conversion_t *ctx)
530
{
531
    SIXELSTATUS status = SIXEL_OK;
498✔
532
    unsigned char *palette;
533
    int palette_colors;
534

535
    ctx->original = NULL;
498✔
536
    ctx->copy = NULL;
498✔
537
    ctx->size = 0;
498✔
538
    ctx->convert_inplace = 0;
498✔
539
    ctx->converted = 0;
498✔
540
    ctx->frame_colorspace = frame_colorspace;
498✔
541

542
    palette = sixel_dither_get_palette(dither);
498✔
543
    palette_colors = sixel_dither_get_num_of_palette_colors(dither);
498✔
544
    ctx->original = palette;
498✔
545

546
    if (palette == NULL || palette_colors <= 0 ||
498!
547
            frame_colorspace == output->colorspace) {
498!
548
        return SIXEL_OK;
498✔
549
    }
550

551
    ctx->size = (size_t)palette_colors * 3;
×
552

553
    output->pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
554
    output->source_colorspace = frame_colorspace;
×
555

556
    if (palette != (unsigned char *)(dither + 1)) {
×
557
        ctx->copy = (unsigned char *)sixel_allocator_malloc(
×
558
            encoder->allocator, ctx->size);
559
        if (ctx->copy == NULL) {
×
560
            sixel_helper_set_additional_message(
×
561
                "sixel_encoder_convert_palette: "
562
                "sixel_allocator_malloc() failed.");
563
            status = SIXEL_BAD_ALLOCATION;
×
564
            goto end;
×
565
        }
566
        memcpy(ctx->copy, palette, ctx->size);
×
567
        dither->palette = ctx->copy;
×
568
    } else {
569
        ctx->convert_inplace = 1;
×
570
    }
571

572
    status = sixel_output_convert_colorspace(output,
×
573
                                             dither->palette,
574
                                             ctx->size);
575
    if (SIXEL_FAILED(status)) {
×
576
        goto end;
×
577
    }
578
    ctx->converted = 1;
×
579

580
end:
581
    output->pixelformat = pixelformat;
×
582
    output->source_colorspace = frame_colorspace;
×
583

584
    return status;
×
585
}
172✔
586

587
static void
588
sixel_encoder_restore_palette(sixel_encoder_t *encoder,
498✔
589
                              sixel_dither_t *dither,
590
                              palette_conversion_t *ctx)
591
{
592
    if (ctx->copy) {
498!
593
        dither->palette = ctx->original;
×
594
        sixel_allocator_free(encoder->allocator, ctx->copy);
×
595
        ctx->copy = NULL;
×
596
    } else if (ctx->convert_inplace && ctx->converted &&
498!
597
               ctx->original && ctx->size > 0) {
×
598
        (void)sixel_helper_convert_colorspace(ctx->original,
×
599
                                              ctx->size,
600
                                              SIXEL_PIXELFORMAT_RGB888,
601
                                              SIXEL_COLORSPACE_GAMMA,
602
                                              ctx->frame_colorspace);
603
    }
604
}
498✔
605

606
static SIXELSTATUS
607
sixel_prepare_builtin_palette(
27✔
608
    sixel_dither_t /* out */ **dither,
609
    int            /* in */  builtin_palette)
610
{
611
    SIXELSTATUS status = SIXEL_FALSE;
27✔
612

613
    *dither = sixel_dither_get(builtin_palette);
27✔
614
    if (*dither == NULL) {
27!
615
        sixel_helper_set_additional_message(
×
616
            "sixel_prepare_builtin_palette: sixel_dither_get() failed.");
617
        status = SIXEL_RUNTIME_ERROR;
×
618
        goto end;
×
619
    }
620

621
    status = SIXEL_OK;
27✔
622

623
end:
18✔
624
    return status;
27✔
625
}
626

627

628
typedef struct sixel_callback_context_for_mapfile {
629
    int reqcolors;
630
    sixel_dither_t *dither;
631
    sixel_allocator_t *allocator;
632
    int working_colorspace;
633
} sixel_callback_context_for_mapfile_t;
634

635

636
/* callback function for sixel_helper_load_image_file() */
637
static SIXELSTATUS
638
load_image_callback_for_palette(
21✔
639
    sixel_frame_t   /* in */    *frame, /* frame object from image loader */
640
    void            /* in */    *data)  /* private data */
641
{
642
    SIXELSTATUS status = SIXEL_FALSE;
21✔
643
    sixel_callback_context_for_mapfile_t *callback_context;
644

645
    /* get callback context object from the private data */
646
    callback_context = (sixel_callback_context_for_mapfile_t *)data;
21✔
647

648
    status = sixel_frame_ensure_colorspace(frame,
28✔
649
                                           callback_context->working_colorspace);
7✔
650
    if (SIXEL_FAILED(status)) {
21!
651
        goto end;
×
652
    }
653

654
    switch (sixel_frame_get_pixelformat(frame)) {
21!
655
    case SIXEL_PIXELFORMAT_PAL1:
2✔
656
    case SIXEL_PIXELFORMAT_PAL2:
657
    case SIXEL_PIXELFORMAT_PAL4:
658
    case SIXEL_PIXELFORMAT_PAL8:
659
        if (sixel_frame_get_palette(frame) == NULL) {
3!
660
            status = SIXEL_LOGIC_ERROR;
×
661
            goto end;
×
662
        }
663
        /* create new dither object */
664
        status = sixel_dither_new(
3✔
665
            &callback_context->dither,
1✔
666
            sixel_frame_get_ncolors(frame),
1✔
667
            callback_context->allocator);
1✔
668
        if (SIXEL_FAILED(status)) {
3!
669
            goto end;
×
670
        }
671

672
        /* use palette which is extracted from the image */
673
        sixel_dither_set_palette(callback_context->dither,
4✔
674
                                 sixel_frame_get_palette(frame));
1✔
675
        /* success */
676
        status = SIXEL_OK;
3✔
677
        break;
3✔
678
    case SIXEL_PIXELFORMAT_G1:
679
        /* use 1bpp grayscale builtin palette */
680
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G1);
×
681
        /* success */
682
        status = SIXEL_OK;
×
683
        break;
×
684
    case SIXEL_PIXELFORMAT_G2:
685
        /* use 2bpp grayscale builtin palette */
686
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G1);
×
687
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G2);
×
688
        /* success */
689
        status = SIXEL_OK;
×
690
        break;
×
691
    case SIXEL_PIXELFORMAT_G4:
692
        /* use 4bpp grayscale builtin palette */
693
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G4);
×
694
        /* success */
695
        status = SIXEL_OK;
×
696
        break;
×
697
    case SIXEL_PIXELFORMAT_G8:
698
        /* use 8bpp grayscale builtin palette */
699
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G8);
×
700
        /* success */
701
        status = SIXEL_OK;
×
702
        break;
×
703
    default:
12✔
704
        /* create new dither object */
705
        status = sixel_dither_new(
18✔
706
            &callback_context->dither,
6✔
707
            callback_context->reqcolors,
6✔
708
            callback_context->allocator);
6✔
709
        if (SIXEL_FAILED(status)) {
18!
710
            goto end;
×
711
        }
712

713
        /* create adaptive palette from given frame object */
714
        status = sixel_dither_initialize(callback_context->dither,
24✔
715
                                         sixel_frame_get_pixels(frame),
6✔
716
                                         sixel_frame_get_width(frame),
6✔
717
                                         sixel_frame_get_height(frame),
6✔
718
                                         sixel_frame_get_pixelformat(frame),
6✔
719
                                         SIXEL_LARGE_NORM,
720
                                         SIXEL_REP_CENTER_BOX,
721
                                         SIXEL_QUALITY_HIGH);
722
        if (SIXEL_FAILED(status)) {
18!
723
            sixel_dither_unref(callback_context->dither);
×
724
            goto end;
×
725
        }
726

727
        /* success */
728
        status = SIXEL_OK;
18✔
729

730
        break;
18✔
731
    }
7✔
732

733
end:
14✔
734
    return status;
21✔
735
}
736

737

738
/* create palette from specified map file */
739
static SIXELSTATUS
740
sixel_prepare_specified_palette(
26✔
741
    sixel_dither_t  /* out */   **dither,
742
    sixel_encoder_t /* in */    *encoder)
743
{
744
    SIXELSTATUS status = SIXEL_FALSE;
26✔
745
    sixel_callback_context_for_mapfile_t callback_context;
746

747
    callback_context.reqcolors = encoder->reqcolors;
26✔
748
    callback_context.dither = NULL;
26✔
749
    callback_context.allocator = encoder->allocator;
26✔
750
    callback_context.working_colorspace = encoder->working_colorspace;
26✔
751

752
    sixel_helper_set_loader_trace(encoder->verbose);
26✔
753
    status = sixel_helper_load_image_file(encoder->mapfile,
36✔
754
                                          1,   /* fstatic */
755
                                          1,   /* fuse_palette */
756
                                          SIXEL_PALETTE_MAX, /* reqcolors */
757
                                          encoder->bgcolor,
10✔
758
                                          SIXEL_LOOP_DISABLE,
759
                                          load_image_callback_for_palette,
760
                                          encoder->finsecure,
10✔
761
                                          encoder->cancel_flag,
26✔
762
                                          &callback_context,
763
                                          encoder->allocator);
10✔
764
    if (status != SIXEL_OK) {
26✔
765
        return status;
5✔
766
    }
767

768
    if (! callback_context.dither) {
21!
769
        sixel_helper_set_additional_message(
×
770
            "sixel_prepare_specified_palette() failed.\n"
771
            "reason: mapfile is empty.");
772
        return SIXEL_BAD_INPUT;
×
773
    }
774

775
    *dither = callback_context.dither;
21✔
776

777
    return status;
21✔
778
}
10✔
779

780

781
/* create dither object from a frame */
782
static SIXELSTATUS
783
sixel_encoder_prepare_palette(
503✔
784
    sixel_encoder_t *encoder,  /* encoder object */
785
    sixel_frame_t   *frame,    /* input frame object */
786
    sixel_dither_t  **dither)  /* dither object to be created from the frame */
787
{
788
    SIXELSTATUS status = SIXEL_FALSE;
503✔
789
    int histogram_colors;
790

791
    switch (encoder->color_option) {
503!
792
    case SIXEL_COLOR_OPTION_HIGHCOLOR:
24✔
793
        if (encoder->dither_cache) {
36!
794
            *dither = encoder->dither_cache;
×
795
            status = SIXEL_OK;
×
796
        } else {
797
            status = sixel_dither_new(dither, (-1), encoder->allocator);
36✔
798
            sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
36✔
799
        }
800
        goto end;
36✔
801
    case SIXEL_COLOR_OPTION_MONOCHROME:
8✔
802
        if (encoder->dither_cache) {
12!
803
            *dither = encoder->dither_cache;
×
804
            status = SIXEL_OK;
×
805
        } else {
806
            status = sixel_prepare_monochrome_palette(dither, encoder->finvert);
12✔
807
        }
808
        goto end;
12✔
809
    case SIXEL_COLOR_OPTION_MAPFILE:
16✔
810
        if (encoder->dither_cache) {
26!
811
            *dither = encoder->dither_cache;
×
812
            status = SIXEL_OK;
×
813
        } else {
814
            status = sixel_prepare_specified_palette(dither, encoder);
26✔
815
        }
816
        goto end;
26✔
817
    case SIXEL_COLOR_OPTION_BUILTIN:
18✔
818
        if (encoder->dither_cache) {
27!
819
            *dither = encoder->dither_cache;
×
820
            status = SIXEL_OK;
×
821
        } else {
822
            status = sixel_prepare_builtin_palette(dither, encoder->builtin_palette);
27✔
823
        }
824
        goto end;
27✔
825
    case SIXEL_COLOR_OPTION_DEFAULT:
402✔
826
    default:
827
        break;
402✔
828
    }
829

830
    if (sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_PALETTE) {
402✔
831
        if (!sixel_frame_get_palette(frame)) {
188!
832
            status = SIXEL_LOGIC_ERROR;
×
833
            goto end;
×
834
        }
835
        status = sixel_dither_new(dither, sixel_frame_get_ncolors(frame),
226✔
836
                                  encoder->allocator);
38✔
837
        if (SIXEL_FAILED(status)) {
188!
838
            goto end;
×
839
        }
840
        sixel_dither_set_palette(*dither, sixel_frame_get_palette(frame));
188✔
841
        sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
188✔
842
        if (sixel_frame_get_transparent(frame) != (-1)) {
188!
843
            sixel_dither_set_transparent(*dither, sixel_frame_get_transparent(frame));
×
844
        }
845
        if (*dither && encoder->dither_cache) {
188!
846
            sixel_dither_unref(encoder->dither_cache);
×
847
        }
848
        goto end;
188✔
849
    }
850

851
    if (sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_GRAYSCALE) {
214!
852
        switch (sixel_frame_get_pixelformat(frame)) {
×
853
        case SIXEL_PIXELFORMAT_G1:
854
            *dither = sixel_dither_get(SIXEL_BUILTIN_G1);
×
855
            break;
×
856
        case SIXEL_PIXELFORMAT_G2:
857
            *dither = sixel_dither_get(SIXEL_BUILTIN_G2);
×
858
            break;
×
859
        case SIXEL_PIXELFORMAT_G4:
860
            *dither = sixel_dither_get(SIXEL_BUILTIN_G4);
×
861
            break;
×
862
        case SIXEL_PIXELFORMAT_G8:
863
            *dither = sixel_dither_get(SIXEL_BUILTIN_G8);
×
864
            break;
×
865
        default:
866
            *dither = NULL;
×
867
            status = SIXEL_LOGIC_ERROR;
×
868
            goto end;
×
869
        }
870
        if (*dither && encoder->dither_cache) {
×
871
            sixel_dither_unref(encoder->dither_cache);
×
872
        }
873
        sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
×
874
        status = SIXEL_OK;
×
875
        goto end;
×
876
    }
877

878
    if (encoder->dither_cache) {
214!
879
        sixel_dither_unref(encoder->dither_cache);
×
880
    }
881
    status = sixel_dither_new(dither, encoder->reqcolors, encoder->allocator);
214✔
882
    if (SIXEL_FAILED(status)) {
214!
883
        goto end;
×
884
    }
885

886
    status = sixel_dither_initialize(*dither,
316✔
887
                                     sixel_frame_get_pixels(frame),
102✔
888
                                     sixel_frame_get_width(frame),
102✔
889
                                     sixel_frame_get_height(frame),
102✔
890
                                     sixel_frame_get_pixelformat(frame),
102✔
891
                                     encoder->method_for_largest,
102✔
892
                                     encoder->method_for_rep,
102✔
893
                                     encoder->quality_mode);
102✔
894
    if (SIXEL_FAILED(status)) {
214!
895
        sixel_dither_unref(*dither);
×
896
        goto end;
×
897
    }
898

899
    histogram_colors = sixel_dither_get_num_of_histogram_colors(*dither);
214✔
900
    if (histogram_colors <= encoder->reqcolors) {
214✔
901
        encoder->method_for_diffuse = SIXEL_DIFFUSE_NONE;
172✔
902
    }
88✔
903
    sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
214✔
904

905
    status = SIXEL_OK;
214✔
906

907
end:
328✔
908
    return status;
503✔
909
}
910

911

912
/* resize a frame with settings of specified encoder object */
913
static SIXELSTATUS
914
sixel_encoder_do_resize(
510✔
915
    sixel_encoder_t /* in */    *encoder,   /* encoder object */
916
    sixel_frame_t   /* in */    *frame)     /* frame object to be resized */
917
{
918
    SIXELSTATUS status = SIXEL_FALSE;
510✔
919
    int src_width;
920
    int src_height;
921
    int dst_width;
922
    int dst_height;
923

924
    /* get frame width and height */
925
    src_width = sixel_frame_get_width(frame);
510✔
926
    src_height = sixel_frame_get_height(frame);
510✔
927

928
    if (src_width < 1) {
510✔
929
         sixel_helper_set_additional_message(
6✔
930
             "sixel_encoder_do_resize: "
931
             "detected a frame with a non-positive width.");
932
        return SIXEL_BAD_ARGUMENT;
6✔
933
    }
934

935
    if (src_height < 1) {
504!
936
         sixel_helper_set_additional_message(
×
937
             "sixel_encoder_do_resize: "
938
             "detected a frame with a non-positive height.");
939
        return SIXEL_BAD_ARGUMENT;
×
940
    }
941

942
    /* settings around scaling */
943
    dst_width = encoder->pixelwidth;    /* may be -1 (default) */
504✔
944
    dst_height = encoder->pixelheight;  /* may be -1 (default) */
504✔
945

946
    /* if the encoder has percentwidth or percentheight property,
947
       convert them to pixelwidth / pixelheight */
948
    if (encoder->percentwidth > 0) {
504✔
949
        dst_width = src_width * encoder->percentwidth / 100;
12✔
950
    }
4✔
951
    if (encoder->percentheight > 0) {
504✔
952
        dst_height = src_height * encoder->percentheight / 100;
9✔
953
    }
3✔
954

955
    /* if only either width or height is set, set also the other
956
       to retain frame aspect ratio */
957
    if (dst_width > 0 && dst_height <= 0) {
504✔
958
        dst_height = src_height * dst_width / src_width;
42✔
959
    }
14✔
960
    if (dst_height > 0 && dst_width <= 0) {
504✔
961
        dst_width = src_width * dst_height / src_height;
33✔
962
    }
11✔
963

964
    /* do resize */
965
    if (dst_width > 0 && dst_height > 0) {
504!
966
        status = sixel_frame_resize(frame, dst_width, dst_height,
124✔
967
                                    encoder->method_for_resampling);
31✔
968
        if (SIXEL_FAILED(status)) {
93!
969
            goto end;
×
970
        }
971
    }
31✔
972

973
    /* success */
974
    status = SIXEL_OK;
504✔
975

976
end:
328✔
977
    return status;
504✔
978
}
178✔
979

980

981
/* clip a frame with settings of specified encoder object */
982
static SIXELSTATUS
983
sixel_encoder_do_clip(
506✔
984
    sixel_encoder_t /* in */    *encoder,   /* encoder object */
985
    sixel_frame_t   /* in */    *frame)     /* frame object to be resized */
986
{
987
    SIXELSTATUS status = SIXEL_FALSE;
506✔
988
    int src_width;
989
    int src_height;
990
    int clip_x;
991
    int clip_y;
992
    int clip_w;
993
    int clip_h;
994

995
    /* get frame width and height */
996
    src_width = sixel_frame_get_width(frame);
506✔
997
    src_height = sixel_frame_get_height(frame);
506✔
998

999
    /* settings around clipping */
1000
    clip_x = encoder->clipx;
506✔
1001
    clip_y = encoder->clipy;
506✔
1002
    clip_w = encoder->clipwidth;
506✔
1003
    clip_h = encoder->clipheight;
506✔
1004

1005
    /* adjust clipping width with comparing it to frame width */
1006
    if (clip_w + clip_x > src_width) {
506✔
1007
        if (clip_x > src_width) {
7✔
1008
            clip_w = 0;
3✔
1009
        } else {
1✔
1010
            clip_w = src_width - clip_x;
4✔
1011
        }
1012
    }
3✔
1013

1014
    /* adjust clipping height with comparing it to frame height */
1015
    if (clip_h + clip_y > src_height) {
506✔
1016
        if (clip_y > src_height) {
6✔
1017
            clip_h = 0;
3✔
1018
        } else {
1✔
1019
            clip_h = src_height - clip_y;
3✔
1020
        }
1021
    }
2✔
1022

1023
    /* do clipping */
1024
    if (clip_w > 0 && clip_h > 0) {
506!
1025
        status = sixel_frame_clip(frame, clip_x, clip_y, clip_w, clip_h);
15✔
1026
        if (SIXEL_FAILED(status)) {
15!
1027
            goto end;
3✔
1028
        }
1029
    }
4✔
1030

1031
    /* success */
1032
    status = SIXEL_OK;
503✔
1033

1034
end:
328✔
1035
    return status;
506✔
1036
}
1037

1038

1039
static void
1040
sixel_debug_print_palette(
3✔
1041
    sixel_dither_t /* in */ *dither /* dithering object */
1042
)
1043
{
1044
    unsigned char *palette;
1045
    int i;
1046

1047
    palette = sixel_dither_get_palette(dither);
3✔
1048
    fprintf(stderr, "palette:\n");
3✔
1049
    for (i = 0; i < sixel_dither_get_num_of_palette_colors(dither); ++i) {
51✔
1050
        fprintf(stderr, "%d: #%02x%02x%02x\n", i,
64✔
1051
                palette[i * 3 + 0],
48✔
1052
                palette[i * 3 + 1],
48✔
1053
                palette[i * 3 + 2]);
48✔
1054
    }
16✔
1055
}
3✔
1056

1057

1058
static SIXELSTATUS
1059
sixel_encoder_output_without_macro(
405✔
1060
    sixel_frame_t       /* in */ *frame,
1061
    sixel_dither_t      /* in */ *dither,
1062
    sixel_output_t      /* in */ *output,
1063
    sixel_encoder_t     /* in */ *encoder)
1064
{
1065
    SIXELSTATUS status = SIXEL_OK;
405✔
1066
    static unsigned char *p;
1067
    int depth;
1068
    enum { message_buffer_size = 256 };
1069
    char message[message_buffer_size];
1070
    int nwrite;
1071
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1072
    int dulation;
1073
    int delay;
1074
    struct timespec tv;
1075
#endif
1076
    unsigned char *pixbuf;
1077
    int width;
1078
    int height;
1079
    int pixelformat = 0;
405✔
1080
    size_t size;
1081
    int frame_colorspace = SIXEL_COLORSPACE_GAMMA;
405✔
1082
    palette_conversion_t palette_ctx;
1083

1084
    memset(&palette_ctx, 0, sizeof(palette_ctx));
405✔
1085
#if defined(HAVE_CLOCK) || defined(HAVE_CLOCK_WIN)
1086
    sixel_clock_t last_clock;
1087
#endif
1088

1089
    if (encoder == NULL) {
405!
1090
        sixel_helper_set_additional_message(
×
1091
            "sixel_encoder_output_without_macro: encoder object is null.");
1092
        status = SIXEL_BAD_ARGUMENT;
×
1093
        goto end;
×
1094
    }
1095

1096
    if (encoder->color_option == SIXEL_COLOR_OPTION_DEFAULT) {
405✔
1097
        sixel_dither_set_optimize_palette(dither, 1);
309✔
1098
    }
103✔
1099

1100
    pixelformat = sixel_frame_get_pixelformat(frame);
405✔
1101
    frame_colorspace = sixel_frame_get_colorspace(frame);
405✔
1102
    output->pixelformat = pixelformat;
405✔
1103
    output->source_colorspace = frame_colorspace;
405✔
1104
    output->colorspace = encoder->output_colorspace;
405✔
1105
    sixel_dither_set_pixelformat(dither, pixelformat);
405✔
1106
    depth = sixel_helper_compute_depth(pixelformat);
405✔
1107
    if (depth < 0) {
405!
1108
        status = SIXEL_LOGIC_ERROR;
×
1109
        nwrite = sprintf(message,
×
1110
                         "sixel_encoder_output_without_macro: "
1111
                         "sixel_helper_compute_depth(%08x) failed.",
1112
                         pixelformat);
1113
        if (nwrite > 0) {
×
1114
            sixel_helper_set_additional_message(message);
×
1115
        }
1116
        goto end;
×
1117
    }
1118

1119
    width = sixel_frame_get_width(frame);
405✔
1120
    height = sixel_frame_get_height(frame);
405✔
1121
    size = (size_t)(width * height * depth);
405✔
1122
    p = (unsigned char *)sixel_allocator_malloc(encoder->allocator, size);
405✔
1123
    if (p == NULL) {
405!
1124
        sixel_helper_set_additional_message(
×
1125
            "sixel_encoder_output_without_macro: sixel_allocator_malloc() failed.");
1126
        status = SIXEL_BAD_ALLOCATION;
×
1127
        goto end;
×
1128
    }
1129
#if defined(HAVE_CLOCK)
1130
    if (output->last_clock == 0) {
405!
1131
        output->last_clock = clock();
405✔
1132
    }
135✔
1133
#elif defined(HAVE_CLOCK_WIN)
1134
    if (output->last_clock == 0) {
1135
        output->last_clock = clock_win();
1136
    }
1137
#endif
1138
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1139
    delay = sixel_frame_get_delay(frame);
405✔
1140
    if (delay > 0 && !encoder->fignore_delay) {
405✔
1141
# if defined(HAVE_CLOCK)
1142
        last_clock = clock();
75✔
1143
/* https://stackoverflow.com/questions/16697005/clock-and-clocks-per-sec-on-osx-10-7 */
1144
#  if defined(__APPLE__)
1145
        dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
146✔
1146
                          / 100000);
73✔
1147
#  else
1148
        dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
2✔
1149
                          / CLOCKS_PER_SEC);
1150
#  endif
1151
        output->last_clock = last_clock;
75✔
1152
# elif defined(HAVE_CLOCK_WIN)
1153
        last_clock = clock_win();
1154
        dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
1155
                          / CLOCKS_PER_SEC);
1156
        output->last_clock = last_clock;
1157
# else
1158
        dulation = 0;
1159
# endif
1160
        if (dulation < 1000 * 10 * delay) {
75!
1161
# if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1162
            tv.tv_sec = 0;
75✔
1163
            tv.tv_nsec = (long)((1000 * 10 * delay - dulation) * 1000);
75✔
1164
#  if defined(HAVE_NANOSLEEP)
1165
            nanosleep(&tv, NULL);
75✔
1166
#  else
1167
            nanosleep_win(&tv, NULL);
1168
#  endif
1169
# endif
1170
        }
73✔
1171
    }
73✔
1172
#endif
1173

1174
    pixbuf = sixel_frame_get_pixels(frame);
405✔
1175
    memcpy(p, pixbuf, (size_t)(width * height * depth));
405✔
1176

1177
    status = sixel_output_convert_colorspace(output, p, size);
405✔
1178
    if (SIXEL_FAILED(status)) {
405!
1179
        goto end;
×
1180
    }
1181

1182
    if (encoder->cancel_flag && *encoder->cancel_flag) {
405!
1183
        goto end;
×
1184
    }
1185

1186
    status = sixel_encoder_convert_palette(encoder,
540✔
1187
                                           output,
135✔
1188
                                           dither,
135✔
1189
                                           frame_colorspace,
135✔
1190
                                           pixelformat,
135✔
1191
                                           &palette_ctx);
1192
    if (SIXEL_FAILED(status)) {
405!
1193
        goto end;
×
1194
    }
1195

1196
    status = sixel_encode(p, width, height, depth, dither, output);
405✔
1197
    if (status != SIXEL_OK) {
405!
1198
        goto end;
×
1199
    }
1200

1201
end:
270✔
1202
    sixel_encoder_restore_palette(encoder, dither, &palette_ctx);
405✔
1203
    output->pixelformat = pixelformat;
405✔
1204
    output->source_colorspace = frame_colorspace;
405✔
1205
    sixel_allocator_free(encoder->allocator, p);
405✔
1206

1207
    return status;
405✔
1208
}
1209

1210

1211
static SIXELSTATUS
1212
sixel_encoder_output_with_macro(
93✔
1213
    sixel_frame_t   /* in */ *frame,
1214
    sixel_dither_t  /* in */ *dither,
1215
    sixel_output_t  /* in */ *output,
1216
    sixel_encoder_t /* in */ *encoder)
1217
{
1218
    SIXELSTATUS status = SIXEL_OK;
93✔
1219
    enum { message_buffer_size = 256 };
1220
    char buffer[message_buffer_size];
1221
    int nwrite;
1222
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1223
    int dulation;
1224
    struct timespec tv;
1225
#endif
1226
    int width;
1227
    int height;
1228
    int pixelformat;
1229
    int depth;
1230
    size_t size = 0;
93✔
1231
    int frame_colorspace = SIXEL_COLORSPACE_GAMMA;
93✔
1232
    unsigned char *converted = NULL;
93✔
1233
    palette_conversion_t palette_ctx;
1234
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1235
    int delay;
1236
#endif
1237
#if defined(HAVE_CLOCK) || defined(HAVE_CLOCK_WIN)
1238
    sixel_clock_t last_clock;
1239
#endif
1240

1241
    memset(&palette_ctx, 0, sizeof(palette_ctx));
93✔
1242

1243
#if defined(HAVE_CLOCK)
1244
    if (output->last_clock == 0) {
93!
1245
        output->last_clock = clock();
93✔
1246
    }
37✔
1247
#elif defined(HAVE_CLOCK_WIN)
1248
    if (output->last_clock == 0) {
1249
        output->last_clock = clock_win();
1250
    }
1251
#endif
1252

1253
    width = sixel_frame_get_width(frame);
93✔
1254
    height = sixel_frame_get_height(frame);
93✔
1255
    pixelformat = sixel_frame_get_pixelformat(frame);
93✔
1256
    depth = sixel_helper_compute_depth(pixelformat);
93✔
1257
    if (depth < 0) {
93!
1258
        status = SIXEL_LOGIC_ERROR;
×
1259
        sixel_helper_set_additional_message(
×
1260
            "sixel_encoder_output_with_macro: "
1261
            "sixel_helper_compute_depth() failed.");
1262
        goto end;
×
1263
    }
1264

1265
    frame_colorspace = sixel_frame_get_colorspace(frame);
93✔
1266
    size = (size_t)width * (size_t)height * (size_t)depth;
93✔
1267
    converted = (unsigned char *)sixel_allocator_malloc(
93✔
1268
        encoder->allocator, size);
37✔
1269
    if (converted == NULL) {
93!
1270
        sixel_helper_set_additional_message(
×
1271
            "sixel_encoder_output_with_macro: "
1272
            "sixel_allocator_malloc() failed.");
1273
        status = SIXEL_BAD_ALLOCATION;
×
1274
        goto end;
×
1275
    }
1276

1277
    memcpy(converted, sixel_frame_get_pixels(frame), size);
93✔
1278
    output->pixelformat = pixelformat;
93✔
1279
    output->source_colorspace = frame_colorspace;
93✔
1280
    output->colorspace = encoder->output_colorspace;
93✔
1281
    status = sixel_output_convert_colorspace(output, converted, size);
93✔
1282
    if (SIXEL_FAILED(status)) {
93!
1283
        goto end;
×
1284
    }
1285

1286
    status = sixel_encoder_convert_palette(encoder,
130✔
1287
                                           output,
37✔
1288
                                           dither,
37✔
1289
                                           frame_colorspace,
37✔
1290
                                           pixelformat,
37✔
1291
                                           &palette_ctx);
1292
    if (SIXEL_FAILED(status)) {
93!
1293
        goto end;
×
1294
    }
1295

1296
    if (sixel_frame_get_loop_no(frame) == 0) {
93✔
1297
        if (encoder->macro_number >= 0) {
55!
1298
            nwrite = sprintf(buffer, "\033P%d;0;1!z",
1✔
1299
                             encoder->macro_number);
1300
        } else {
1✔
1301
            nwrite = sprintf(buffer, "\033P%d;0;1!z",
54✔
1302
                             sixel_frame_get_frame_no(frame));
1303
        }
1304
        if (nwrite < 0) {
55!
1305
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1306
            sixel_helper_set_additional_message(
×
1307
                "sixel_encoder_output_with_macro: sprintf() failed.");
1308
            goto end;
×
1309
        }
1310
        nwrite = sixel_write_callback(buffer,
74✔
1311
                                      (int)strlen(buffer),
55✔
1312
                                      &encoder->outfd);
55✔
1313
        if (nwrite < 0) {
55!
1314
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1315
            sixel_helper_set_additional_message(
×
1316
                "sixel_encoder_output_with_macro: "
1317
                "sixel_write_callback() failed.");
1318
            goto end;
×
1319
        }
1320

1321
        status = sixel_encode(converted,
74✔
1322
                              width,
19✔
1323
                              height,
19✔
1324
                              depth,
19✔
1325
                              dither,
19✔
1326
                              output);
19✔
1327
        if (SIXEL_FAILED(status)) {
55!
1328
            goto end;
×
1329
        }
1330

1331
        nwrite = sixel_write_callback("\033\\", 2, &encoder->outfd);
55✔
1332
        if (nwrite < 0) {
55!
1333
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1334
            sixel_helper_set_additional_message(
×
1335
                "sixel_encoder_output_with_macro: "
1336
                "sixel_write_callback() failed.");
1337
            goto end;
×
1338
        }
1339
    }
19✔
1340
    if (encoder->macro_number < 0) {
129✔
1341
        nwrite = sprintf(buffer, "\033[%d*z",
90✔
1342
                         sixel_frame_get_frame_no(frame));
1343
        if (nwrite < 0) {
90!
1344
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1345
            sixel_helper_set_additional_message(
×
1346
                "sixel_encoder_output_with_macro: sprintf() failed.");
1347
        }
1348
        nwrite = sixel_write_callback(buffer,
126✔
1349
                                      (int)strlen(buffer),
90✔
1350
                                      &encoder->outfd);
90✔
1351
        if (nwrite < 0) {
90!
1352
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1353
            sixel_helper_set_additional_message(
×
1354
                "sixel_encoder_output_with_macro: "
1355
                "sixel_write_callback() failed.");
1356
            goto end;
×
1357
        }
1358
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1359
        delay = sixel_frame_get_delay(frame);
90✔
1360
        if (delay > 0 && !encoder->fignore_delay) {
90!
1361
# if defined(HAVE_CLOCK)
1362
            last_clock = clock();
63✔
1363
/* https://stackoverflow.com/questions/16697005/clock-and-clocks-per-sec-on-osx-10-7 */
1364
#  if defined(__APPLE__)
1365
            dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
54✔
1366
                             / 100000);
27✔
1367
#  else
1368
            dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
36✔
1369
                             / CLOCKS_PER_SEC);
1370
#  endif
1371
            output->last_clock = last_clock;
63✔
1372
# elif defined(HAVE_CLOCK_WIN)
1373
            last_clock = clock_win();
1374
            dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
1375
                             / CLOCKS_PER_SEC);
1376
            output->last_clock = last_clock;
1377
# else
1378
            dulation = 0;
1379
# endif
1380
            if (dulation < 1000 * 10 * delay) {
63!
1381
# if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1382
                tv.tv_sec = 0;
60✔
1383
                tv.tv_nsec = (long)((1000 * 10 * delay - dulation) * 1000);
60✔
1384
#  if defined(HAVE_NANOSLEEP)
1385
                nanosleep(&tv, NULL);
60✔
1386
#  else
1387
                nanosleep_win(&tv, NULL);
1388
#  endif
1389
# endif
1390
            }
24✔
1391
        }
27✔
1392
#endif
1393
    }
36✔
1394

1395
end:
20✔
1396
    sixel_encoder_restore_palette(encoder, dither, &palette_ctx);
93✔
1397
    output->pixelformat = pixelformat;
93✔
1398
    output->source_colorspace = frame_colorspace;
93✔
1399
    sixel_allocator_free(encoder->allocator, converted);
93✔
1400

1401
    return status;
93✔
1402
}
1403

1404

1405
static SIXELSTATUS
1406
sixel_encoder_emit_iso2022_chars(
×
1407
    sixel_encoder_t *encoder,
1408
    sixel_frame_t *frame
1409
)
1410
{
1411
    char *buf_p, *buf;
1412
    int col, row;
1413
    int charset = encoder->start_dscs;
×
1414
    int is_96cs = 0;
×
1415
    unsigned int code;
1416
    int num_cols, num_rows;
1417
    SIXELSTATUS status;
1418
    size_t alloc_size;
1419
    int nwrite;
1420
    int target_fd;
1421

1422
    code = 0x100020 + (is_96cs ? 0x80 : 0) + charset * 0x100;
×
1423
    num_cols = (sixel_frame_get_width(frame) + encoder->cell_width - 1)
×
1424
             / encoder->cell_width;
×
1425
    num_rows = (sixel_frame_get_height(frame) + encoder->cell_height - 1)
×
1426
             / encoder->cell_height;
×
1427

1428
    /* cols x rows + designation(4 chars) + SI + SO + LFs */
1429
    alloc_size = num_cols * num_rows + (num_cols * num_rows / 96 + 1) * 4 + 2 + num_rows;
×
1430
    buf_p = buf = sixel_allocator_malloc(encoder->allocator, alloc_size);
×
1431
    if (buf == NULL) {
×
1432
        sixel_helper_set_additional_message(
×
1433
            "sixel_encoder_emit_iso2022_chars: sixel_allocator_malloc() failed.");
1434
        status = SIXEL_BAD_ALLOCATION;
×
1435
        goto end;
×
1436
    }
1437

1438
    code = 0x20;
×
1439
    *(buf_p++) = '\016';  /* SI */
×
1440
    *(buf_p++) = '\033';
×
1441
    *(buf_p++) = ')';
×
1442
    *(buf_p++) = ' ';
×
1443
    *(buf_p++) = charset;
×
1444
    for(row = 0; row < num_rows; row++) {
×
1445
        for(col = 0; col < num_cols; col++) {
×
1446
            if ((code & 0x7f) == 0x0) {
×
1447
                if (charset == 0x7e) {
×
1448
                    is_96cs = 1 - is_96cs;
×
1449
                    charset = '0';
×
1450
                } else {
1451
                    charset++;
×
1452
                }
1453
                code = 0x20;
×
1454
                *(buf_p++) = '\033';
×
1455
                *(buf_p++) = is_96cs ? '-': ')';
×
1456
                *(buf_p++) = ' ';
×
1457
                *(buf_p++) = charset;
×
1458
            }
1459
            *(buf_p++) = code++;
×
1460
        }
1461
        *(buf_p++) = '\n';
×
1462
    }
1463
    *(buf_p++) = '\017';  /* SO */
×
1464

1465
    if (encoder->tile_outfd >= 0) {
×
1466
        target_fd = encoder->tile_outfd;
×
1467
    } else {
1468
        target_fd = encoder->outfd;
×
1469
    }
1470

1471
    nwrite = write(target_fd, buf, buf_p - buf);
×
1472
    if (nwrite != buf_p - buf) {
×
1473
        sixel_helper_set_additional_message(
×
1474
            "sixel_encoder_emit_iso2022_chars: write() failed.");
1475
        status = SIXEL_RUNTIME_ERROR;
×
1476
        goto end;
×
1477
    }
1478

1479
    sixel_allocator_free(encoder->allocator, buf);
×
1480

1481
    status = SIXEL_OK;
×
1482

1483
end:
1484
    return status;
×
1485
}
1486

1487

1488
/*
1489
 * This routine is derived from mlterm's drcssixel.c
1490
 * (https://raw.githubusercontent.com/arakiken/mlterm/master/drcssixel/drcssixel.c).
1491
 * The original implementation is credited to Araki Ken.
1492
 * Adjusted here to integrate with libsixel's encoder pipeline.
1493
 */
1494
static SIXELSTATUS
1495
sixel_encoder_emit_drcsmmv2_chars(
×
1496
    sixel_encoder_t *encoder,
1497
    sixel_frame_t *frame
1498
)
1499
{
1500
    char *buf_p, *buf;
1501
    int col, row;
1502
    int charset = encoder->start_dscs;
×
1503
    int is_96cs = 0;
×
1504
    unsigned int code;
1505
    int num_cols, num_rows;
1506
    SIXELSTATUS status;
1507
    size_t alloc_size;
1508
    int nwrite;
1509
    int target_fd;
1510

1511
    code = 0x100020 + (is_96cs ? 0x80 : 0) + charset * 0x100;
×
1512
    num_cols = (sixel_frame_get_width(frame) + encoder->cell_width - 1)
×
1513
             / encoder->cell_width;
×
1514
    num_rows = (sixel_frame_get_height(frame) + encoder->cell_height - 1)
×
1515
             / encoder->cell_height;
×
1516

1517
    /* cols x rows x 4(out of BMP) + rows(LFs) */
1518
    alloc_size = num_cols * num_rows * 4 + num_rows;
×
1519
    buf_p = buf = sixel_allocator_malloc(encoder->allocator, alloc_size);
×
1520
    if (buf == NULL) {
×
1521
        sixel_helper_set_additional_message(
×
1522
            "sixel_encoder_emit_drcsmmv2_chars: sixel_allocator_malloc() failed.");
1523
        status = SIXEL_BAD_ALLOCATION;
×
1524
        goto end;
×
1525
    }
1526

1527
    for(row = 0; row < num_rows; row++) {
×
1528
        for(col = 0; col < num_cols; col++) {
×
1529
            *(buf_p++) = ((code >> 18) & 0x07) | 0xf0;
×
1530
            *(buf_p++) = ((code >> 12) & 0x3f) | 0x80;
×
1531
            *(buf_p++) = ((code >> 6) & 0x3f) | 0x80;
×
1532
            *(buf_p++) = (code & 0x3f) | 0x80;
×
1533
            code++;
×
1534
            if ((code & 0x7f) == 0x0) {
×
1535
                if (charset == 0x7e) {
×
1536
                    is_96cs = 1 - is_96cs;
×
1537
                    charset = '0';
×
1538
                } else {
1539
                    charset++;
×
1540
                }
1541
                code = 0x100020 + (is_96cs ? 0x80 : 0) + charset * 0x100;
×
1542
            }
1543
        }
1544
        *(buf_p++) = '\n';
×
1545
    }
1546

1547
    if (charset == 0x7e) {
×
1548
        is_96cs = 1 - is_96cs;
×
1549
    } else {
1550
        charset = '0';
×
1551
        charset++;
×
1552
    }
1553
    if (encoder->tile_outfd >= 0) {
×
1554
        target_fd = encoder->tile_outfd;
×
1555
    } else {
1556
        target_fd = encoder->outfd;
×
1557
    }
1558

1559
    nwrite = write(target_fd, buf, buf_p - buf);
×
1560
    if (nwrite != buf_p - buf) {
×
1561
        sixel_helper_set_additional_message(
×
1562
            "sixel_encoder_emit_drcsmmv2_chars: write() failed.");
1563
        status = SIXEL_RUNTIME_ERROR;
×
1564
        goto end;
×
1565
    }
1566

1567
    sixel_allocator_free(encoder->allocator, buf);
×
1568

1569
    status = SIXEL_OK;
×
1570

1571
end:
1572
    return status;
×
1573
}
1574

1575
static SIXELSTATUS
1576
sixel_encoder_encode_frame(
512✔
1577
    sixel_encoder_t *encoder,
1578
    sixel_frame_t   *frame,
1579
    sixel_output_t  *output)
1580
{
1581
    SIXELSTATUS status = SIXEL_FALSE;
512✔
1582
    sixel_dither_t *dither = NULL;
512✔
1583
    int height;
1584
    int is_animation = 0;
512✔
1585
    int nwrite;
1586
    char buf[256];
1587
    sixel_write_function fn_write = NULL;
512✔
1588

1589
    /* evaluate -w, -h, and -c option: crop/scale input source */
1590
    if (encoder->clipfirst) {
512✔
1591
        /* clipping */
1592
        status = sixel_encoder_do_clip(encoder, frame);
8✔
1593
        if (SIXEL_FAILED(status)) {
8!
1594
            goto end;
2✔
1595
        }
1596

1597
        /* scaling */
1598
        status = sixel_encoder_do_resize(encoder, frame);
6✔
1599
        if (SIXEL_FAILED(status)) {
6!
1600
            goto end;
×
1601
        }
1602
    } else {
2✔
1603
        /* scaling */
1604
        status = sixel_encoder_do_resize(encoder, frame);
504✔
1605
        if (SIXEL_FAILED(status)) {
504✔
1606
            goto end;
6✔
1607
        }
1608

1609
        /* clipping */
1610
        status = sixel_encoder_do_clip(encoder, frame);
498✔
1611
        if (SIXEL_FAILED(status)) {
498!
1612
            goto end;
1✔
1613
        }
1614
    }
1615

1616
    status = sixel_frame_ensure_colorspace(frame,
678✔
1617
                                           encoder->working_colorspace);
175✔
1618
    if (SIXEL_FAILED(status)) {
503!
1619
        goto end;
×
1620
    }
1621

1622
    /* prepare dither context */
1623
    status = sixel_encoder_prepare_palette(encoder, frame, &dither);
503✔
1624
    if (status != SIXEL_OK) {
503✔
1625
        dither = NULL;
5✔
1626
        goto end;
5✔
1627
    }
1628

1629
    if (encoder->dither_cache != NULL) {
498!
1630
        encoder->dither_cache = dither;
×
1631
        sixel_dither_ref(dither);
×
1632
    }
1633

1634
    if (encoder->fdrcs) {
498!
1635
        status = sixel_encoder_ensure_cell_size(encoder);
×
1636
        if (SIXEL_FAILED(status)) {
×
1637
            goto end;
×
1638
        }
1639
        if (encoder->fuse_macro || encoder->macro_number >= 0) {
×
1640
            sixel_helper_set_additional_message(
×
1641
                "drcs option cannot be used together with macro output.");
1642
            status = SIXEL_BAD_ARGUMENT;
×
1643
            goto end;
×
1644
        }
1645
    }
1646

1647
    /* evaluate -v option: print palette */
1648
    if (encoder->verbose) {
498✔
1649
        if ((sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_PALETTE)) {
9✔
1650
            sixel_debug_print_palette(dither);
3✔
1651
        }
1✔
1652
    }
3✔
1653

1654
    /* evaluate -d option: set method for diffusion */
1655
    sixel_dither_set_diffusion_type(dither, encoder->method_for_diffuse);
498✔
1656
    sixel_dither_set_diffusion_scan(dither, encoder->method_for_scan);
498✔
1657
    sixel_dither_set_diffusion_carry(dither, encoder->method_for_carry);
498✔
1658

1659
    /* evaluate -C option: set complexion score */
1660
    if (encoder->complexion > 1) {
498✔
1661
        sixel_dither_set_complexion_score(dither, encoder->complexion);
6✔
1662
    }
2✔
1663

1664
    if (output) {
498!
1665
        sixel_output_ref(output);
×
1666
    } else {
1667
        /* create output context */
1668
        if (encoder->fuse_macro || encoder->macro_number >= 0) {
498✔
1669
            /* -u or -n option */
1670
            fn_write = sixel_hex_write_callback;
93✔
1671
        } else {
37✔
1672
            fn_write = sixel_write_callback;
405✔
1673
        }
1674
        status = sixel_output_new(&output,
498✔
1675
                                  fn_write,
172✔
1676
                                  &encoder->outfd,
498✔
1677
                                  encoder->allocator);
172✔
1678
        if (SIXEL_FAILED(status)) {
498!
1679
            goto end;
×
1680
        }
1681
    }
1682

1683
    if (encoder->fdrcs) {
498!
1684
        sixel_output_set_skip_dcs_envelope(output, 1);
×
1685
        sixel_output_set_skip_header(output, 1);
×
1686
    }
1687

1688
    sixel_output_set_8bit_availability(output, encoder->f8bit);
498✔
1689
    sixel_output_set_gri_arg_limit(output, encoder->has_gri_arg_limit);
498✔
1690
    sixel_output_set_palette_type(output, encoder->palette_type);
498✔
1691
    sixel_output_set_penetrate_multiplexer(
498✔
1692
        output, encoder->penetrate_multiplexer);
172✔
1693
    sixel_output_set_encode_policy(output, encoder->encode_policy);
498✔
1694
    sixel_output_set_ormode(output, encoder->ormode);
498✔
1695

1696
    if (sixel_frame_get_multiframe(frame) && !encoder->fstatic) {
498!
1697
        if (sixel_frame_get_loop_no(frame) != 0 || sixel_frame_get_frame_no(frame) != 0) {
117✔
1698
            is_animation = 1;
108✔
1699
        }
42✔
1700
        height = sixel_frame_get_height(frame);
117✔
1701
        (void) sixel_tty_scroll(sixel_write_callback, encoder->outfd, height, is_animation);
117✔
1702
    }
45✔
1703

1704
    if (encoder->cancel_flag && *encoder->cancel_flag) {
498!
1705
        status = SIXEL_INTERRUPTED;
×
1706
        goto end;
×
1707
    }
1708

1709
    if (encoder->fdrcs) {  /* -@ option */
498!
1710
        nwrite = sprintf(buf,
×
1711
                         "%s%s1;0;0;%d;1;3;%d;0{ %c",
1712
                         (encoder->drcs_mmv > 0) ? (
×
1713
                             encoder->f8bit ? "\233?8800h": "\033[?8800h"
×
1714
                         ): "",
1715
                         encoder->f8bit ? "\220": "\033P",
1716
                         encoder->cell_width,
1717
                         encoder->cell_height,
1718
                         encoder->start_dscs);
×
1719
        if (nwrite < 0) {
×
1720
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1721
            sixel_helper_set_additional_message(
×
1722
                "sixel_encoder_encode_frame: sprintf() failed.");
1723
            goto end;
×
1724
        }
1725
        nwrite = fn_write(buf, nwrite, &encoder->outfd);
×
1726
        if (nwrite < 0) {
×
1727
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1728
            sixel_helper_set_additional_message(
×
1729
                "sixel_encoder_encode_frame: write() failed.");
1730
            goto end;
×
1731
        }
1732
    }
1733

1734
    /* output sixel: junction of multi-frame processing strategy */
1735
    if (encoder->fuse_macro) {  /* -u option */
498✔
1736
        /* use macro */
1737
        status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
90✔
1738
    } else if (encoder->macro_number >= 0) { /* -n option */
444✔
1739
        /* use macro */
1740
        status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
3✔
1741
    } else {
1✔
1742
        /* do not use macro */
1743
        status = sixel_encoder_output_without_macro(frame, dither, output, encoder);
405✔
1744
    }
1745
    if (SIXEL_FAILED(status)) {
498!
1746
        goto end;
×
1747
    }
1748

1749
    if (encoder->cancel_flag && *encoder->cancel_flag) {
498!
1750
        nwrite = sixel_write_callback("\x18\033\\", 3, &encoder->outfd);
×
1751
        if (nwrite < 0) {
×
1752
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1753
            sixel_helper_set_additional_message(
×
1754
                "sixel_encoder_encode_frame: sixel_write_callback() failed.");
1755
            goto end;
×
1756
        }
1757
        status = SIXEL_INTERRUPTED;
×
1758
    }
1759
    if (SIXEL_FAILED(status)) {
498!
1760
        goto end;
×
1761
    }
1762

1763
    if (encoder->fdrcs) {  /* -@ option */
498!
1764
        if (encoder->f8bit) {
×
1765
            nwrite = fn_write("\234", 1, &encoder->outfd);
×
1766
        } else {
1767
            nwrite = fn_write("\033\\", 2, &encoder->outfd);
×
1768
        }
1769
        if (nwrite < 0) {
×
1770
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1771
            sixel_helper_set_additional_message(
×
1772
                "sixel_encoder_encode_frame: fn_write() failed.");
1773
            goto end;
×
1774
        }
1775

1776
        if (encoder->tile_outfd >= 0) {
×
1777
            if (encoder->drcs_mmv == 0) {
×
1778
                status = sixel_encoder_emit_iso2022_chars(encoder, frame);
×
1779
                if (SIXEL_FAILED(status)) {
×
1780
                    goto end;
×
1781
                }
1782
            } else {
1783
                status = sixel_encoder_emit_drcsmmv2_chars(encoder, frame);
×
1784
                if (SIXEL_FAILED(status)) {
×
1785
                    goto end;
×
1786
                }
1787
            }
1788
        }
1789
    }
1790

1791

1792
end:
326✔
1793
    if (output) {
512✔
1794
        sixel_output_unref(output);
498✔
1795
    }
172✔
1796
    if (dither) {
512✔
1797
        sixel_dither_unref(dither);
498✔
1798
    }
172✔
1799

1800
    return status;
512✔
1801
}
1802

1803

1804
/* create encoder object */
1805
SIXELAPI SIXELSTATUS
1806
sixel_encoder_new(
498✔
1807
    sixel_encoder_t     /* out */ **ppencoder, /* encoder object to be created */
1808
    sixel_allocator_t   /* in */  *allocator)  /* allocator, null if you use
1809
                                                  default allocator */
1810
{
1811
    SIXELSTATUS status = SIXEL_FALSE;
498✔
1812
    char const *env_default_bgcolor = NULL;
498✔
1813
    char const *env_default_ncolors = NULL;
498✔
1814
    int ncolors;
1815
#if HAVE__DUPENV_S
1816
    errno_t e;
1817
    size_t len;
1818
#endif  /* HAVE__DUPENV_S */
1819

1820
    if (allocator == NULL) {
498!
1821
        status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
498✔
1822
        if (SIXEL_FAILED(status)) {
498!
1823
            goto end;
×
1824
        }
1825
    } else {
166✔
1826
        sixel_allocator_ref(allocator);
×
1827
    }
1828

1829
    *ppencoder
166✔
1830
        = (sixel_encoder_t *)sixel_allocator_malloc(allocator,
664✔
1831
                                                    sizeof(sixel_encoder_t));
1832
    if (*ppencoder == NULL) {
498!
1833
        sixel_helper_set_additional_message(
×
1834
            "sixel_encoder_new: sixel_allocator_malloc() failed.");
1835
        status = SIXEL_BAD_ALLOCATION;
×
1836
        sixel_allocator_unref(allocator);
×
1837
        goto end;
×
1838
    }
1839

1840
    (*ppencoder)->ref                   = 1;
498✔
1841
    (*ppencoder)->reqcolors             = (-1);
498✔
1842
    (*ppencoder)->mapfile               = NULL;
498✔
1843
    (*ppencoder)->color_option          = SIXEL_COLOR_OPTION_DEFAULT;
498✔
1844
    (*ppencoder)->builtin_palette       = 0;
498✔
1845
    (*ppencoder)->method_for_diffuse    = SIXEL_DIFFUSE_AUTO;
498✔
1846
    (*ppencoder)->method_for_scan       = SIXEL_SCAN_AUTO;
498✔
1847
    (*ppencoder)->method_for_carry      = SIXEL_CARRY_AUTO;
498✔
1848
    (*ppencoder)->method_for_largest    = SIXEL_LARGE_AUTO;
498✔
1849
    (*ppencoder)->method_for_rep        = SIXEL_REP_AUTO;
498✔
1850
    (*ppencoder)->quality_mode          = SIXEL_QUALITY_AUTO;
498✔
1851
    (*ppencoder)->method_for_resampling = SIXEL_RES_BILINEAR;
498✔
1852
    (*ppencoder)->loop_mode             = SIXEL_LOOP_AUTO;
498✔
1853
    (*ppencoder)->palette_type          = SIXEL_PALETTETYPE_AUTO;
498✔
1854
    (*ppencoder)->f8bit                 = 0;
498✔
1855
    (*ppencoder)->has_gri_arg_limit     = 0;
498✔
1856
    (*ppencoder)->finvert               = 0;
498✔
1857
    (*ppencoder)->fuse_macro            = 0;
498✔
1858
    (*ppencoder)->fdrcs                 = 0;
498✔
1859
    (*ppencoder)->fignore_delay         = 0;
498✔
1860
    (*ppencoder)->complexion            = 1;
498✔
1861
    (*ppencoder)->fstatic               = 0;
498✔
1862
    (*ppencoder)->cell_width            = 0;
498✔
1863
    (*ppencoder)->cell_height           = 0;
498✔
1864
    (*ppencoder)->pixelwidth            = (-1);
498✔
1865
    (*ppencoder)->pixelheight           = (-1);
498✔
1866
    (*ppencoder)->percentwidth          = (-1);
498✔
1867
    (*ppencoder)->percentheight         = (-1);
498✔
1868
    (*ppencoder)->clipx                 = 0;
498✔
1869
    (*ppencoder)->clipy                 = 0;
498✔
1870
    (*ppencoder)->clipwidth             = 0;
498✔
1871
    (*ppencoder)->clipheight            = 0;
498✔
1872
    (*ppencoder)->clipfirst             = 0;
498✔
1873
    (*ppencoder)->macro_number          = (-1);
498✔
1874
    (*ppencoder)->verbose               = 0;
498✔
1875
    (*ppencoder)->penetrate_multiplexer = 0;
498✔
1876
    (*ppencoder)->encode_policy         = SIXEL_ENCODEPOLICY_AUTO;
498✔
1877
    (*ppencoder)->working_colorspace    = SIXEL_COLORSPACE_GAMMA;
498✔
1878
    (*ppencoder)->output_colorspace     = SIXEL_COLORSPACE_GAMMA;
498✔
1879
    (*ppencoder)->ormode                = 0;
498✔
1880
    (*ppencoder)->pipe_mode             = 0;
498✔
1881
    (*ppencoder)->bgcolor               = NULL;
498✔
1882
    (*ppencoder)->outfd                 = STDOUT_FILENO;
498✔
1883
    (*ppencoder)->tile_outfd            = (-1);
498✔
1884
    (*ppencoder)->finsecure             = 0;
498✔
1885
    (*ppencoder)->cancel_flag           = NULL;
498✔
1886
    (*ppencoder)->dither_cache          = NULL;
498✔
1887
    (*ppencoder)->start_dscs            = '0';
498✔
1888
    (*ppencoder)->drcs_mmv              = 2;
498✔
1889
    (*ppencoder)->allocator             = allocator;
498✔
1890

1891
    /* evaluate environment variable ${SIXEL_BGCOLOR} */
1892
#if HAVE__DUPENV_S
1893
    e = _dupenv_s(&env_default_bgcolor, &len, "SIXEL_BGCOLOR");
1894
    if (e != (0)) {
1895
        sixel_helper_set_additional_message(
1896
            "failed to get environment variable $SIXEL_BGCOLOR.");
1897
        return (SIXEL_LIBC_ERROR | (e & 0xff));
1898
    }
1899
#else
1900
    env_default_bgcolor = getenv("SIXEL_BGCOLOR");
498✔
1901
#endif  /* HAVE__DUPENV_S */
1902
    if (env_default_bgcolor != NULL) {
498!
1903
        status = sixel_parse_x_colorspec(&(*ppencoder)->bgcolor,
×
1904
                                         env_default_bgcolor,
1905
                                         allocator);
1906
        if (SIXEL_FAILED(status)) {
×
1907
            goto error;
×
1908
        }
1909
    }
1910

1911
    /* evaluate environment variable ${SIXEL_COLORS} */
1912
#if HAVE__DUPENV_S
1913
    e = _dupenv_s(&env_default_bgcolor, &len, "SIXEL_COLORS");
1914
    if (e != (0)) {
1915
        sixel_helper_set_additional_message(
1916
            "failed to get environment variable $SIXEL_COLORS.");
1917
        return (SIXEL_LIBC_ERROR | (e & 0xff));
1918
    }
1919
#else
1920
    env_default_ncolors = getenv("SIXEL_COLORS");
498✔
1921
#endif  /* HAVE__DUPENV_S */
1922
    if (env_default_ncolors) {
498!
1923
        ncolors = atoi(env_default_ncolors); /* may overflow */
×
1924
        if (ncolors > 1 && ncolors <= SIXEL_PALETTE_MAX) {
×
1925
            (*ppencoder)->reqcolors = ncolors;
×
1926
        }
1927
    }
1928

1929
    /* success */
1930
    status = SIXEL_OK;
498✔
1931

1932
    goto end;
498✔
1933

1934
error:
1935
    sixel_allocator_free(allocator, *ppencoder);
×
1936
    sixel_allocator_unref(allocator);
×
1937
    *ppencoder = NULL;
×
1938

1939
end:
332✔
1940
#if HAVE__DUPENV_S
1941
    free(env_default_bgcolor);
1942
    free(env_default_ncolors);
1943
#endif  /* HAVE__DUPENV_S */
1944
    return status;
498✔
1945
}
1946

1947

1948
/* create encoder object (deprecated version) */
1949
SIXELAPI /* deprecated */ sixel_encoder_t *
1950
sixel_encoder_create(void)
×
1951
{
1952
    SIXELSTATUS status = SIXEL_FALSE;
×
1953
    sixel_encoder_t *encoder = NULL;
×
1954

1955
    status = sixel_encoder_new(&encoder, NULL);
×
1956
    if (SIXEL_FAILED(status)) {
×
1957
        return NULL;
×
1958
    }
1959

1960
    return encoder;
×
1961
}
1962

1963

1964
/* destroy encoder object */
1965
static void
1966
sixel_encoder_destroy(sixel_encoder_t *encoder)
498✔
1967
{
1968
    sixel_allocator_t *allocator;
1969

1970
    if (encoder) {
498!
1971
        allocator = encoder->allocator;
498✔
1972
        sixel_allocator_free(allocator, encoder->mapfile);
498✔
1973
        sixel_allocator_free(allocator, encoder->bgcolor);
498✔
1974
        sixel_dither_unref(encoder->dither_cache);
498✔
1975
        if (encoder->outfd
501!
1976
            && encoder->outfd != STDOUT_FILENO
498!
1977
            && encoder->outfd != STDERR_FILENO) {
172!
1978
#if HAVE__CLOSE
1979
            (void) _close(encoder->outfd);
1980
#else
1981
            (void) close(encoder->outfd);
9✔
1982
#endif  /* HAVE__CLOSE */
1983
        }
3✔
1984
        if (encoder->tile_outfd >= 0
498!
1985
            && encoder->tile_outfd != encoder->outfd
166!
1986
            && encoder->tile_outfd != STDOUT_FILENO
×
1987
            && encoder->tile_outfd != STDERR_FILENO) {
×
1988
#if HAVE__CLOSE
1989
            (void) _close(encoder->tile_outfd);
1990
#else
1991
            (void) close(encoder->tile_outfd);
×
1992
#endif  /* HAVE__CLOSE */
1993
        }
1994
        sixel_allocator_free(allocator, encoder);
498✔
1995
        sixel_allocator_unref(allocator);
498✔
1996
    }
166✔
1997
}
498✔
1998

1999

2000
/* increase reference count of encoder object (thread-unsafe) */
2001
SIXELAPI void
2002
sixel_encoder_ref(sixel_encoder_t *encoder)
1,080✔
2003
{
2004
    /* TODO: be thread safe */
2005
    ++encoder->ref;
1,080✔
2006
}
1,080✔
2007

2008

2009
/* decrease reference count of encoder object (thread-unsafe) */
2010
SIXELAPI void
2011
sixel_encoder_unref(sixel_encoder_t *encoder)
1,578✔
2012
{
2013
    /* TODO: be thread safe */
2014
    if (encoder != NULL && --encoder->ref == 0) {
1,578!
2015
        sixel_encoder_destroy(encoder);
498✔
2016
    }
166✔
2017
}
1,578✔
2018

2019

2020
/* set cancel state flag to encoder object */
2021
SIXELAPI SIXELSTATUS
2022
sixel_encoder_set_cancel_flag(
417✔
2023
    sixel_encoder_t /* in */ *encoder,
2024
    int             /* in */ *cancel_flag
2025
)
2026
{
2027
    SIXELSTATUS status = SIXEL_OK;
417✔
2028

2029
    encoder->cancel_flag = cancel_flag;
417✔
2030

2031
    return status;
417✔
2032
}
2033

2034

2035
/* set an option flag to encoder object */
2036
SIXELAPI SIXELSTATUS
2037
sixel_encoder_setopt(
663✔
2038
    sixel_encoder_t /* in */ *encoder,
2039
    int             /* in */ arg,
2040
    char const      /* in */ *value)
2041
{
2042
    SIXELSTATUS status = SIXEL_FALSE;
663✔
2043
    int number;
2044
    int parsed;
2045
    char unit[32];
2046
    char lowered[16];
2047
    size_t len;
2048
    size_t i;
2049

2050
    sixel_encoder_ref(encoder);
663✔
2051

2052
    switch(arg) {
663!
2053
    case SIXEL_OPTFLAG_OUTFILE:  /* o */
6✔
2054
        if (*value == '\0') {
9!
2055
            sixel_helper_set_additional_message(
×
2056
                "no file name specified.");
2057
            status = SIXEL_BAD_ARGUMENT;
×
2058
            goto end;
×
2059
        }
2060
        if (strcmp(value, "-") != 0) {
9!
2061
            if (encoder->outfd && encoder->outfd != STDOUT_FILENO) {
9!
2062
#if HAVE__CLOSE
2063
                (void) _close(encoder->outfd);
2064
#else
2065
                (void) close(encoder->outfd);
×
2066
#endif  /* HAVE__CLOSE */
2067
            }
2068
#if HAVE__OPEN
2069
            encoder->outfd = _open(value,
2070
                                   O_RDWR|O_CREAT|O_TRUNC,
2071
                                   S_IRUSR|S_IWUSR);
2072
#else
2073
            encoder->outfd = open(value,
9✔
2074
                                  O_RDWR|O_CREAT|O_TRUNC,
2075
                                  S_IRUSR|S_IWUSR);
2076
#endif  /* HAVE__OPEN */
2077
        }
3✔
2078
        break;
9✔
2079
    case SIXEL_OPTFLAG_ALT_CHARSET_PATH:  /* T */
2080
        if (*value == '\0') {
×
2081
            sixel_helper_set_additional_message(
×
2082
                "no file name specified.");
2083
            status = SIXEL_BAD_ARGUMENT;
×
2084
            goto end;
×
2085
        }
2086
        if (encoder->tile_outfd >= 0
×
2087
            && encoder->tile_outfd != encoder->outfd
×
2088
            && encoder->tile_outfd != STDOUT_FILENO
×
2089
            && encoder->tile_outfd != STDERR_FILENO) {
×
2090
#if HAVE__CLOSE
2091
            (void) _close(encoder->tile_outfd);
2092
#else
2093
            (void) close(encoder->tile_outfd);
×
2094
#endif  /* HAVE__CLOSE */
2095
        }
2096
        encoder->tile_outfd = (-1);
×
2097
        if (strcmp(value, "-") == 0) {
×
2098
            encoder->tile_outfd = STDOUT_FILENO;
×
2099
        } else {
2100
#if HAVE__OPEN
2101
            encoder->tile_outfd = _open(value,
2102
                                           O_RDWR|O_CREAT|O_TRUNC,
2103
                                           S_IRUSR|S_IWUSR);
2104
#else
2105
            encoder->tile_outfd = open(value,
×
2106
                                          O_RDWR|O_CREAT|O_TRUNC,
2107
                                          S_IRUSR|S_IWUSR);
2108
#endif  /* HAVE__OPEN */
2109
            if (encoder->tile_outfd < 0) {
×
2110
                sixel_helper_set_additional_message(
×
2111
                    "sixel_encoder_setopt: failed to open tile"
2112
                    " output path.");
2113
                status = SIXEL_RUNTIME_ERROR;
×
2114
                goto end;
×
2115
            }
2116
        }
2117
        break;
×
2118
    case SIXEL_OPTFLAG_7BIT_MODE:  /* 7 */
10✔
2119
        encoder->f8bit = 0;
15✔
2120
        break;
15✔
2121
    case SIXEL_OPTFLAG_8BIT_MODE:  /* 8 */
12✔
2122
        encoder->f8bit = 1;
18✔
2123
        break;
18✔
2124
    case SIXEL_OPTFLAG_HAS_GRI_ARG_LIMIT:  /* R */
2125
        encoder->has_gri_arg_limit = 1;
×
2126
        break;
×
2127
    case SIXEL_OPTFLAG_COLORS:  /* p */
18✔
2128
        encoder->reqcolors = atoi(value);
27✔
2129
        if (encoder->complexion < 1) {
27!
2130
            sixel_helper_set_additional_message(
×
2131
                "-p/--colors parameter must be 1 or more.");
2132
            status = SIXEL_BAD_ARGUMENT;
×
2133
            goto end;
×
2134
        } else if (encoder->complexion > 256) {
27!
2135
            sixel_helper_set_additional_message(
×
2136
                "-p/--colors parameter must be less then or equal to 256.");
2137
            status = SIXEL_BAD_ARGUMENT;
×
2138
            goto end;
×
2139
        }
2140
        break;
27✔
2141
    case SIXEL_OPTFLAG_MAPFILE:  /* m */
18✔
2142
        if (encoder->mapfile) {
27✔
2143
            sixel_allocator_free(encoder->allocator, encoder->mapfile);
3✔
2144
        }
1✔
2145
        encoder->mapfile = arg_strdup(value, encoder->allocator);
27✔
2146
        if (encoder->mapfile == NULL) {
27!
2147
            sixel_helper_set_additional_message(
×
2148
                "sixel_encoder_setopt: sixel_allocator_malloc() failed.");
2149
            status = SIXEL_BAD_ALLOCATION;
×
2150
            goto end;
×
2151
        }
2152
        encoder->color_option = SIXEL_COLOR_OPTION_MAPFILE;
27✔
2153
        break;
27✔
2154
    case SIXEL_OPTFLAG_MONOCHROME:  /* e */
10✔
2155
        encoder->color_option = SIXEL_COLOR_OPTION_MONOCHROME;
15✔
2156
        break;
15✔
2157
    case SIXEL_OPTFLAG_HIGH_COLOR:  /* I */
28✔
2158
        encoder->color_option = SIXEL_COLOR_OPTION_HIGHCOLOR;
42✔
2159
        break;
42✔
2160
    case SIXEL_OPTFLAG_BUILTIN_PALETTE:  /* b */
22✔
2161
        if (strcmp(value, "xterm16") == 0) {
33✔
2162
            encoder->builtin_palette = SIXEL_BUILTIN_XTERM16;
6✔
2163
        } else if (strcmp(value, "xterm256") == 0) {
29✔
2164
            encoder->builtin_palette = SIXEL_BUILTIN_XTERM256;
6✔
2165
        } else if (strcmp(value, "vt340mono") == 0) {
23✔
2166
            encoder->builtin_palette = SIXEL_BUILTIN_VT340_MONO;
3✔
2167
        } else if (strcmp(value, "vt340color") == 0) {
19✔
2168
            encoder->builtin_palette = SIXEL_BUILTIN_VT340_COLOR;
3✔
2169
        } else if (strcmp(value, "gray1") == 0) {
16✔
2170
            encoder->builtin_palette = SIXEL_BUILTIN_G1;
3✔
2171
        } else if (strcmp(value, "gray2") == 0) {
13✔
2172
            encoder->builtin_palette = SIXEL_BUILTIN_G2;
3✔
2173
        } else if (strcmp(value, "gray4") == 0) {
10✔
2174
            encoder->builtin_palette = SIXEL_BUILTIN_G4;
3✔
2175
        } else if (strcmp(value, "gray8") == 0) {
7✔
2176
            encoder->builtin_palette = SIXEL_BUILTIN_G8;
3✔
2177
        } else {
1✔
2178
            sixel_helper_set_additional_message(
3✔
2179
                    "cannot parse builtin palette option.");
2180
            status = SIXEL_BAD_ARGUMENT;
3✔
2181
            goto end;
3✔
2182
        }
2183
        encoder->color_option = SIXEL_COLOR_OPTION_BUILTIN;
30✔
2184
        break;
30✔
2185
    case SIXEL_OPTFLAG_DIFFUSION:  /* d */
46✔
2186
        /* parse --diffusion option */
2187
        if (strcmp(value, "auto") == 0) {
69✔
2188
            encoder->method_for_diffuse = SIXEL_DIFFUSE_AUTO;
3✔
2189
        } else if (strcmp(value, "none") == 0) {
67✔
2190
            encoder->method_for_diffuse = SIXEL_DIFFUSE_NONE;
15✔
2191
        } else if (strcmp(value, "fs") == 0) {
56✔
2192
            encoder->method_for_diffuse = SIXEL_DIFFUSE_FS;
6✔
2193
        } else if (strcmp(value, "atkinson") == 0) {
47✔
2194
            encoder->method_for_diffuse = SIXEL_DIFFUSE_ATKINSON;
12✔
2195
        } else if (strcmp(value, "jajuni") == 0) {
37✔
2196
            encoder->method_for_diffuse = SIXEL_DIFFUSE_JAJUNI;
6✔
2197
        } else if (strcmp(value, "stucki") == 0) {
29✔
2198
            encoder->method_for_diffuse = SIXEL_DIFFUSE_STUCKI;
6✔
2199
        } else if (strcmp(value, "burkes") == 0) {
23✔
2200
            encoder->method_for_diffuse = SIXEL_DIFFUSE_BURKES;
6✔
2201
        } else if (strcmp(value, "a_dither") == 0) {
17✔
2202
            encoder->method_for_diffuse = SIXEL_DIFFUSE_A_DITHER;
6✔
2203
        } else if (strcmp(value, "x_dither") == 0) {
11✔
2204
            encoder->method_for_diffuse = SIXEL_DIFFUSE_X_DITHER;
6✔
2205
        } else if (strcmp(value, "lso1") == 0) {
5!
2206
            encoder->method_for_diffuse = SIXEL_DIFFUSE_LSO1;
×
2207
        } else if (strcmp(value, "lso2") == 0) {
3!
2208
            encoder->method_for_diffuse = SIXEL_DIFFUSE_LSO2;
×
2209
        } else if (strcmp(value, "lso3") == 0) {
3!
2210
            encoder->method_for_diffuse = SIXEL_DIFFUSE_LSO3;
×
2211
        } else {
2212
            sixel_helper_set_additional_message(
3✔
2213
                "specified diffusion method is not supported.");
2214
            status = SIXEL_BAD_ARGUMENT;
3✔
2215
            goto end;
3✔
2216
        }
2217
        break;
66✔
2218
    case SIXEL_OPTFLAG_DIFFUSION_SCAN:  /* y */
2219
        if (strcmp(value, "auto") == 0) {
×
2220
            encoder->method_for_scan = SIXEL_SCAN_AUTO;
×
2221
        } else if (strcmp(value, "serpentine") == 0) {
×
2222
            encoder->method_for_scan = SIXEL_SCAN_SERPENTINE;
×
2223
        } else if (strcmp(value, "raster") == 0) {
×
2224
            encoder->method_for_scan = SIXEL_SCAN_RASTER;
×
2225
        } else {
2226
            sixel_helper_set_additional_message(
×
2227
                "specified diffusion scan is not supported.");
2228
            status = SIXEL_BAD_ARGUMENT;
×
2229
            goto end;
×
2230
        }
2231
        break;
×
2232
    case SIXEL_OPTFLAG_DIFFUSION_CARRY:  /* Y */
2233
        if (strcmp(value, "auto") == 0) {
×
2234
            encoder->method_for_carry = SIXEL_CARRY_AUTO;
×
2235
        } else if (strcmp(value, "direct") == 0) {
×
2236
            encoder->method_for_carry = SIXEL_CARRY_DISABLE;
×
2237
        } else if (strcmp(value, "carry") == 0) {
×
2238
            encoder->method_for_carry = SIXEL_CARRY_ENABLE;
×
2239
        } else {
2240
            sixel_helper_set_additional_message(
×
2241
                "specified diffusion carry mode is not supported.");
2242
            status = SIXEL_BAD_ARGUMENT;
×
2243
            goto end;
×
2244
        }
2245
        break;
×
2246
    case SIXEL_OPTFLAG_FIND_LARGEST:  /* f */
10✔
2247
        /* parse --find-largest option */
2248
        if (value) {
15!
2249
            if (strcmp(value, "auto") == 0) {
15✔
2250
                encoder->method_for_largest = SIXEL_LARGE_AUTO;
3✔
2251
            } else if (strcmp(value, "norm") == 0) {
13✔
2252
                encoder->method_for_largest = SIXEL_LARGE_NORM;
6✔
2253
            } else if (strcmp(value, "lum") == 0) {
8✔
2254
                encoder->method_for_largest = SIXEL_LARGE_LUM;
3✔
2255
            } else {
1✔
2256
                sixel_helper_set_additional_message(
3✔
2257
                    "specified finding method is not supported.");
2258
                status = SIXEL_BAD_ARGUMENT;
3✔
2259
                goto end;
3✔
2260
            }
2261
        }
4✔
2262
        break;
12✔
2263
    case SIXEL_OPTFLAG_SELECT_COLOR:  /* s */
10✔
2264
        /* parse --select-color option */
2265
        if (strcmp(value, "auto") == 0) {
15✔
2266
            encoder->method_for_rep = SIXEL_REP_AUTO;
3✔
2267
        } else if (strcmp(value, "center") == 0) {
13✔
2268
            encoder->method_for_rep = SIXEL_REP_CENTER_BOX;
3✔
2269
        } else if (strcmp(value, "average") == 0) {
10✔
2270
            encoder->method_for_rep = SIXEL_REP_AVERAGE_COLORS;
3✔
2271
        } else if ((strcmp(value, "histogram") == 0) ||
7!
2272
                   (strcmp(value, "histgram") == 0)) {
3!
2273
            encoder->method_for_rep = SIXEL_REP_AVERAGE_PIXELS;
3✔
2274
        } else {
1✔
2275
            sixel_helper_set_additional_message(
3✔
2276
                "specified finding method is not supported.");
2277
            status = SIXEL_BAD_ARGUMENT;
3✔
2278
            goto end;
3✔
2279
        }
2280
        break;
12✔
2281
    case SIXEL_OPTFLAG_CROP:  /* c */
10✔
2282
#if HAVE_SSCANF_S
2283
        number = sscanf_s(value, "%dx%d+%d+%d",
2284
                          &encoder->clipwidth, &encoder->clipheight,
2285
                          &encoder->clipx, &encoder->clipy);
2286
#else
2287
        number = sscanf(value, "%dx%d+%d+%d",
20✔
2288
                        &encoder->clipwidth, &encoder->clipheight,
5✔
2289
                        &encoder->clipx, &encoder->clipy);
5✔
2290
#endif  /* HAVE_SSCANF_S */
2291
        if (number != 4) {
15!
2292
            status = SIXEL_BAD_ARGUMENT;
×
2293
            goto end;
×
2294
        }
2295
        if (encoder->clipwidth <= 0 || encoder->clipheight <= 0) {
15!
2296
            status = SIXEL_BAD_ARGUMENT;
×
2297
            goto end;
×
2298
        }
2299
        if (encoder->clipx < 0 || encoder->clipy < 0) {
15!
2300
            status = SIXEL_BAD_ARGUMENT;
×
2301
            goto end;
×
2302
        }
2303
        encoder->clipfirst = 0;
15✔
2304
        break;
15✔
2305
    case SIXEL_OPTFLAG_WIDTH:  /* w */
50✔
2306
#if HAVE_SSCANF_S
2307
        parsed = sscanf_s(value, "%d%2s", &number, unit, sizeof(unit) - 1);
2308
#else
2309
        parsed = sscanf(value, "%d%2s", &number, unit);
75✔
2310
#endif  /* HAVE_SSCANF_S */
2311
        if (parsed == 2 && strcmp(unit, "%") == 0) {
75!
2312
            encoder->pixelwidth = (-1);
12✔
2313
            encoder->percentwidth = number;
12✔
2314
        } else if (parsed == 1 || (parsed == 2 && strcmp(unit, "px") == 0)) {
67!
2315
            encoder->pixelwidth = number;
51✔
2316
            encoder->percentwidth = (-1);
51✔
2317
        } else if (strcmp(value, "auto") == 0) {
29✔
2318
            encoder->pixelwidth = (-1);
9✔
2319
            encoder->percentwidth = (-1);
9✔
2320
        } else {
3✔
2321
            sixel_helper_set_additional_message(
3✔
2322
                "cannot parse -w/--width option.");
2323
            status = SIXEL_BAD_ARGUMENT;
3✔
2324
            goto end;
3✔
2325
        }
2326
        if (encoder->clipwidth) {
72✔
2327
            encoder->clipfirst = 1;
6✔
2328
        }
2✔
2329
        break;
72✔
2330
    case SIXEL_OPTFLAG_HEIGHT:  /* h */
44✔
2331
#if HAVE_SSCANF_S
2332
        parsed = sscanf_s(value, "%d%2s", &number, unit, sizeof(unit) - 1);
2333
#else
2334
        parsed = sscanf(value, "%d%2s", &number, unit);
66✔
2335
#endif  /* HAVE_SSCANF_S */
2336
        if (parsed == 2 && strcmp(unit, "%") == 0) {
66!
2337
            encoder->pixelheight = (-1);
9✔
2338
            encoder->percentheight = number;
9✔
2339
        } else if (parsed == 1 || (parsed == 2 && strcmp(unit, "px") == 0)) {
60!
2340
            encoder->pixelheight = number;
45✔
2341
            encoder->percentheight = (-1);
45✔
2342
        } else if (strcmp(value, "auto") == 0) {
27✔
2343
            encoder->pixelheight = (-1);
9✔
2344
            encoder->percentheight = (-1);
9✔
2345
        } else {
3✔
2346
            sixel_helper_set_additional_message(
3✔
2347
                "cannot parse -h/--height option.");
2348
            status = SIXEL_BAD_ARGUMENT;
3✔
2349
            goto end;
3✔
2350
        }
2351
        if (encoder->clipheight) {
63✔
2352
            encoder->clipfirst = 1;
3✔
2353
        }
1✔
2354
        break;
63✔
2355
    case SIXEL_OPTFLAG_RESAMPLING:  /* r */
34✔
2356
        /* parse --resampling option */
2357
        if (strcmp(value, "nearest") == 0) {
51✔
2358
            encoder->method_for_resampling = SIXEL_RES_NEAREST;
18✔
2359
        } else if (strcmp(value, "gaussian") == 0) {
39✔
2360
            encoder->method_for_resampling = SIXEL_RES_GAUSSIAN;
3✔
2361
        } else if (strcmp(value, "hanning") == 0) {
31✔
2362
            encoder->method_for_resampling = SIXEL_RES_HANNING;
3✔
2363
        } else if (strcmp(value, "hamming") == 0) {
28✔
2364
            encoder->method_for_resampling = SIXEL_RES_HAMMING;
3✔
2365
        } else if (strcmp(value, "bilinear") == 0) {
25✔
2366
            encoder->method_for_resampling = SIXEL_RES_BILINEAR;
3✔
2367
        } else if (strcmp(value, "welsh") == 0) {
22✔
2368
            encoder->method_for_resampling = SIXEL_RES_WELSH;
3✔
2369
        } else if (strcmp(value, "bicubic") == 0) {
19✔
2370
            encoder->method_for_resampling = SIXEL_RES_BICUBIC;
3✔
2371
        } else if (strcmp(value, "lanczos2") == 0) {
16✔
2372
            encoder->method_for_resampling = SIXEL_RES_LANCZOS2;
6✔
2373
        } else if (strcmp(value, "lanczos3") == 0) {
11✔
2374
            encoder->method_for_resampling = SIXEL_RES_LANCZOS3;
3✔
2375
        } else if (strcmp(value, "lanczos4") == 0) {
7✔
2376
            encoder->method_for_resampling = SIXEL_RES_LANCZOS4;
3✔
2377
        } else {
1✔
2378
            sixel_helper_set_additional_message(
3✔
2379
                "specified desampling method is not supported.");
2380
            status = SIXEL_BAD_ARGUMENT;
3✔
2381
            goto end;
3✔
2382
        }
2383
        break;
48✔
2384
    case SIXEL_OPTFLAG_QUALITY:  /* q */
12✔
2385
        /* parse --quality option */
2386
        if (strcmp(value, "auto") == 0) {
18✔
2387
            encoder->quality_mode = SIXEL_QUALITY_AUTO;
6✔
2388
        } else if (strcmp(value, "high") == 0) {
14✔
2389
            encoder->quality_mode = SIXEL_QUALITY_HIGH;
3✔
2390
        } else if (strcmp(value, "low") == 0) {
10✔
2391
            encoder->quality_mode = SIXEL_QUALITY_LOW;
3✔
2392
        } else if (strcmp(value, "full") == 0) {
7✔
2393
            encoder->quality_mode = SIXEL_QUALITY_FULL;
3✔
2394
        } else {
1✔
2395
            sixel_helper_set_additional_message(
3✔
2396
                "cannot parse quality option.");
2397
            status = SIXEL_BAD_ARGUMENT;
3✔
2398
            goto end;
3✔
2399
        }
2400
        break;
15✔
2401
    case SIXEL_OPTFLAG_LOOPMODE:  /* l */
10✔
2402
        /* parse --loop-control option */
2403
        if (strcmp(value, "auto") == 0) {
15✔
2404
            encoder->loop_mode = SIXEL_LOOP_AUTO;
3✔
2405
        } else if (strcmp(value, "force") == 0) {
13!
2406
            encoder->loop_mode = SIXEL_LOOP_FORCE;
×
2407
        } else if (strcmp(value, "disable") == 0) {
12✔
2408
            encoder->loop_mode = SIXEL_LOOP_DISABLE;
9✔
2409
        } else {
3✔
2410
            sixel_helper_set_additional_message(
3✔
2411
                "cannot parse loop-control option.");
2412
            status = SIXEL_BAD_ARGUMENT;
3✔
2413
            goto end;
3✔
2414
        }
2415
        break;
12✔
2416
    case SIXEL_OPTFLAG_PALETTE_TYPE:  /* t */
18✔
2417
        /* parse --palette-type option */
2418
        if (strcmp(value, "auto") == 0) {
27✔
2419
            encoder->palette_type = SIXEL_PALETTETYPE_AUTO;
6✔
2420
        } else if (strcmp(value, "hls") == 0) {
23✔
2421
            encoder->palette_type = SIXEL_PALETTETYPE_HLS;
15✔
2422
        } else if (strcmp(value, "rgb") == 0) {
11✔
2423
            encoder->palette_type = SIXEL_PALETTETYPE_RGB;
3✔
2424
        } else {
1✔
2425
            sixel_helper_set_additional_message(
3✔
2426
                "cannot parse palette type option.");
2427
            status = SIXEL_BAD_ARGUMENT;
3✔
2428
            goto end;
3✔
2429
        }
2430
        break;
24✔
2431
    case SIXEL_OPTFLAG_BGCOLOR:  /* B */
30✔
2432
        /* parse --bgcolor option */
2433
        if (encoder->bgcolor) {
45✔
2434
            sixel_allocator_free(encoder->allocator, encoder->bgcolor);
6✔
2435
            encoder->bgcolor = NULL;
6✔
2436
        }
2✔
2437
        status = sixel_parse_x_colorspec(&encoder->bgcolor,
60✔
2438
                                         value,
15✔
2439
                                         encoder->allocator);
15✔
2440
        if (SIXEL_FAILED(status)) {
45✔
2441
            sixel_helper_set_additional_message(
21✔
2442
                "cannot parse bgcolor option.");
2443
            status = SIXEL_BAD_ARGUMENT;
21✔
2444
            goto end;
21✔
2445
        }
2446
        break;
24✔
2447
    case SIXEL_OPTFLAG_INSECURE:  /* k */
2448
        encoder->finsecure = 1;
×
2449
        break;
×
2450
    case SIXEL_OPTFLAG_INVERT:  /* i */
4✔
2451
        encoder->finvert = 1;
6✔
2452
        break;
6✔
2453
    case SIXEL_OPTFLAG_USE_MACRO:  /* u */
4✔
2454
        encoder->fuse_macro = 1;
6✔
2455
        break;
6✔
2456
    case SIXEL_OPTFLAG_MACRO_NUMBER:  /* n */
2✔
2457
        encoder->macro_number = atoi(value);
3✔
2458
        if (encoder->macro_number < 0) {
3!
2459
            status = SIXEL_BAD_ARGUMENT;
×
2460
            goto end;
×
2461
        }
2462
        break;
3✔
2463
    case SIXEL_OPTFLAG_IGNORE_DELAY:  /* g */
4✔
2464
        encoder->fignore_delay = 1;
6✔
2465
        break;
6✔
2466
    case SIXEL_OPTFLAG_VERBOSE:  /* v */
6✔
2467
        encoder->verbose = 1;
9✔
2468
        sixel_helper_set_loader_trace(1);
9✔
2469
        break;
9✔
2470
    case SIXEL_OPTFLAG_STATIC:  /* S */
2✔
2471
        encoder->fstatic = 1;
3✔
2472
        break;
3✔
2473
    case SIXEL_OPTFLAG_DRCS:  /* @ */
2474
        encoder->fdrcs = 1;
×
2475
        if (strlen(value) == 1 && value[0] >= 32) {
×
2476
            encoder->start_dscs = value[0];
×
2477
        } else {
2478
            sixel_helper_set_additional_message(
×
2479
                "cannot parse DSCS option.");
2480
            status = SIXEL_BAD_ARGUMENT;
×
2481
            goto end;
×
2482
        }
2483
        break;
×
2484
    case SIXEL_OPTFLAG_MAPPING_VERSION:  /* M */
2485
        encoder->drcs_mmv = atoi(value);
×
2486
        if (encoder->drcs_mmv < 0 || encoder->drcs_mmv >= 3) {
×
2487
            sixel_helper_set_additional_message(
×
2488
                "unknown DRCS unicode mapping version.");
2489
            status = SIXEL_BAD_ARGUMENT;
×
2490
            goto end;
×
2491
        }
2492
        break;
×
2493
    case SIXEL_OPTFLAG_PENETRATE:  /* P */
6✔
2494
        encoder->penetrate_multiplexer = 1;
9✔
2495
        break;
9✔
2496
    case SIXEL_OPTFLAG_ENCODE_POLICY:  /* E */
8✔
2497
        if (strcmp(value, "auto") == 0) {
12✔
2498
            encoder->encode_policy = SIXEL_ENCODEPOLICY_AUTO;
3✔
2499
        } else if (strcmp(value, "fast") == 0) {
10✔
2500
            encoder->encode_policy = SIXEL_ENCODEPOLICY_FAST;
3✔
2501
        } else if (strcmp(value, "size") == 0) {
7✔
2502
            encoder->encode_policy = SIXEL_ENCODEPOLICY_SIZE;
3✔
2503
        } else {
1✔
2504
            sixel_helper_set_additional_message(
3✔
2505
                "cannot parse encode policy option.");
2506
            status = SIXEL_BAD_ARGUMENT;
3✔
2507
            goto end;
3✔
2508
        }
2509
        break;
9✔
2510
    case SIXEL_OPTFLAG_WORKING_COLORSPACE:  /* W */
2511
        if (value == NULL) {
×
2512
            sixel_helper_set_additional_message(
×
2513
                "working-colorspace requires an argument.");
2514
            status = SIXEL_BAD_ARGUMENT;
×
2515
            goto end;
×
2516
        } else {
2517
            len = strlen(value);
×
2518

2519
            if (len >= sizeof(lowered)) {
×
2520
                sixel_helper_set_additional_message(
×
2521
                    "specified working colorspace name is too long.");
2522
                status = SIXEL_BAD_ARGUMENT;
×
2523
                goto end;
×
2524
            }
2525
            for (i = 0; i < len; ++i) {
×
2526
                lowered[i] = (char)tolower((unsigned char)value[i]);
×
2527
            }
2528
            lowered[len] = '\0';
×
2529

2530
            if (strcmp(lowered, "gamma") == 0) {
×
2531
                encoder->working_colorspace = SIXEL_COLORSPACE_GAMMA;
×
2532
            } else if (strcmp(lowered, "linear") == 0) {
×
2533
                encoder->working_colorspace = SIXEL_COLORSPACE_LINEAR;
×
2534
            } else if (strcmp(lowered, "oklab") == 0) {
×
2535
                encoder->working_colorspace = SIXEL_COLORSPACE_OKLAB;
×
2536
            } else {
2537
                sixel_helper_set_additional_message(
×
2538
                    "unsupported working colorspace specified.");
2539
                status = SIXEL_BAD_ARGUMENT;
×
2540
                goto end;
×
2541
            }
2542
        }
2543
        break;
×
2544
    case SIXEL_OPTFLAG_OUTPUT_COLORSPACE:  /* U */
2545
        if (value == NULL) {
×
2546
            sixel_helper_set_additional_message(
×
2547
                "output-colorspace requires an argument.");
2548
            status = SIXEL_BAD_ARGUMENT;
×
2549
            goto end;
×
2550
        } else {
2551
            len = strlen(value);
×
2552

2553
            if (len >= sizeof(lowered)) {
×
2554
                sixel_helper_set_additional_message(
×
2555
                    "specified output colorspace name is too long.");
2556
                status = SIXEL_BAD_ARGUMENT;
×
2557
                goto end;
×
2558
            }
2559
            for (i = 0; i < len; ++i) {
×
2560
                lowered[i] = (char)tolower((unsigned char)value[i]);
×
2561
            }
2562
            lowered[len] = '\0';
×
2563

2564
            if (strcmp(lowered, "gamma") == 0) {
×
2565
                encoder->output_colorspace = SIXEL_COLORSPACE_GAMMA;
×
2566
            } else if (strcmp(lowered, "linear") == 0) {
×
2567
                encoder->output_colorspace = SIXEL_COLORSPACE_LINEAR;
×
2568
            } else if (strcmp(lowered, "smpte-c") == 0 ||
×
2569
                       strcmp(lowered, "smptec") == 0) {
×
2570
                encoder->output_colorspace = SIXEL_COLORSPACE_SMPTEC;
×
2571
            } else {
2572
                sixel_helper_set_additional_message(
×
2573
                    "unsupported output colorspace specified.");
2574
                status = SIXEL_BAD_ARGUMENT;
×
2575
                goto end;
×
2576
            }
2577
        }
2578
        break;
×
2579
    case SIXEL_OPTFLAG_ORMODE:  /* O */
2580
        encoder->ormode = 1;
×
2581
        break;
×
2582
    case SIXEL_OPTFLAG_COMPLEXION_SCORE:  /* C */
6✔
2583
        encoder->complexion = atoi(value);
9✔
2584
        if (encoder->complexion < 1) {
9✔
2585
            sixel_helper_set_additional_message(
3✔
2586
                "complexion parameter must be 1 or more.");
2587
            status = SIXEL_BAD_ARGUMENT;
3✔
2588
            goto end;
3✔
2589
        }
2590
        break;
6✔
2591
    case SIXEL_OPTFLAG_PIPE_MODE:  /* D */
2592
        encoder->pipe_mode = 1;
×
2593
        break;
×
2594
    case '?':  /* unknown option */
3✔
2595
    default:
2596
        /* exit if unknown options are specified */
2597
        sixel_helper_set_additional_message(
3✔
2598
            "unknown option is specified.");
2599
        status = SIXEL_BAD_ARGUMENT;
3✔
2600
        goto end;
3✔
2601
    }
2602

2603
    /* detects arguments conflictions */
2604
    if (encoder->reqcolors != (-1)) {
603✔
2605
        switch (encoder->color_option) {
96!
2606
        case SIXEL_COLOR_OPTION_MAPFILE:
2607
            sixel_helper_set_additional_message(
×
2608
                "option -p, --colors conflicts with -m, --mapfile.");
2609
            status = SIXEL_BAD_ARGUMENT;
×
2610
            goto end;
×
2611
        case SIXEL_COLOR_OPTION_MONOCHROME:
2✔
2612
            sixel_helper_set_additional_message(
3✔
2613
                "option -e, --monochrome conflicts with -p, --colors.");
2614
            status = SIXEL_BAD_ARGUMENT;
3✔
2615
            goto end;
3✔
2616
        case SIXEL_COLOR_OPTION_HIGHCOLOR:
2✔
2617
            sixel_helper_set_additional_message(
3✔
2618
                "option -p, --colors conflicts with -I, --high-color.");
2619
            status = SIXEL_BAD_ARGUMENT;
3✔
2620
            goto end;
3✔
2621
        case SIXEL_COLOR_OPTION_BUILTIN:
2✔
2622
            sixel_helper_set_additional_message(
3✔
2623
                "option -p, --colors conflicts with -b, --builtin-palette.");
2624
            status = SIXEL_BAD_ARGUMENT;
3✔
2625
            goto end;
3✔
2626
        default:
58✔
2627
            break;
87✔
2628
        }
2629
    }
29✔
2630

2631
    /* 8bit output option(-8) conflicts width GNU Screen integration(-P) */
2632
    if (encoder->f8bit && encoder->penetrate_multiplexer) {
594✔
2633
        sixel_helper_set_additional_message(
3✔
2634
            "option -8 --8bit-mode conflicts"
2635
            " with -P, --penetrate.");
2636
        status = SIXEL_BAD_ARGUMENT;
3✔
2637
        goto end;
3✔
2638
    }
2639

2640
    status = SIXEL_OK;
591✔
2641

2642
end:
442✔
2643
    sixel_encoder_unref(encoder);
663✔
2644

2645
    return status;
663✔
2646
}
2647

2648

2649
/* called when image loader component load a image frame */
2650
static SIXELSTATUS
2651
load_image_callback(sixel_frame_t *frame, void *data)
512✔
2652
{
2653
    return sixel_encoder_encode_frame((sixel_encoder_t *)data, frame, NULL);
512✔
2654
}
2655

2656

2657
/* load source data from specified file and encode it to SIXEL format
2658
 * output to encoder->outfd */
2659
SIXELAPI SIXELSTATUS
2660
sixel_encoder_encode(
417✔
2661
    sixel_encoder_t *encoder,   /* encoder object */
2662
    char const      *filename)  /* input filename */
2663
{
2664
    SIXELSTATUS status = SIXEL_FALSE;
417✔
2665
    int fuse_palette = 1;
417✔
2666

2667
    if (encoder == NULL) {
417!
2668
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2669
#  pragma GCC diagnostic push
2670
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2671
#endif
2672
        encoder = sixel_encoder_create();
×
2673
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2674
#  pragma GCC diagnostic pop
2675
#endif
2676
        if (encoder == NULL) {
×
2677
            sixel_helper_set_additional_message(
×
2678
                "sixel_encoder_encode: sixel_encoder_create() failed.");
2679
            status = SIXEL_BAD_ALLOCATION;
×
2680
            goto end;
×
2681
        }
2682
    } else {
2683
        sixel_encoder_ref(encoder);
417✔
2684
    }
2685

2686
    /* if required color is not set, set the max value */
2687
    if (encoder->reqcolors == (-1)) {
417✔
2688
        encoder->reqcolors = SIXEL_PALETTE_MAX;
399✔
2689
    }
133✔
2690

2691
    /* if required color is less then 2, set the min value */
2692
    if (encoder->reqcolors < 2) {
417✔
2693
        encoder->reqcolors = SIXEL_PALETTE_MIN;
3✔
2694
    }
1✔
2695

2696
    /* if color space option is not set, choose RGB color space */
2697
    if (encoder->palette_type == SIXEL_PALETTETYPE_AUTO) {
417✔
2698
        encoder->palette_type = SIXEL_PALETTETYPE_RGB;
399✔
2699
    }
133✔
2700

2701
    /* if color option is not default value, prohibit to read
2702
       the file as a paletted image */
2703
    if (encoder->color_option != SIXEL_COLOR_OPTION_DEFAULT) {
417✔
2704
        fuse_palette = 0;
99✔
2705
    }
33✔
2706

2707
    /* if scaling options are set, prohibit to read the file as
2708
       a paletted image */
2709
    if (encoder->percentwidth > 0 ||
533✔
2710
        encoder->percentheight > 0 ||
405✔
2711
        encoder->pixelwidth > 0 ||
399✔
2712
        encoder->pixelheight > 0) {
381✔
2713
        fuse_palette = 0;
99✔
2714
    }
33✔
2715

2716
reload:
278✔
2717
    sixel_helper_set_loader_trace(encoder->verbose);
417✔
2718
    status = sixel_helper_load_image_file(filename,
556✔
2719
                                          encoder->fstatic,
139✔
2720
                                          fuse_palette,
139✔
2721
                                          encoder->reqcolors,
139✔
2722
                                          encoder->bgcolor,
139✔
2723
                                          encoder->loop_mode,
139✔
2724
                                          load_image_callback,
2725
                                          encoder->finsecure,
139✔
2726
                                          encoder->cancel_flag,
417✔
2727
                                          (void *)encoder,
139✔
2728
                                          encoder->allocator);
139✔
2729
    if (status != SIXEL_OK) {
417✔
2730
        goto end;
24✔
2731
    }
2732

2733
    if (encoder->pipe_mode) {
393!
2734
#if HAVE_CLEARERR
2735
        clearerr(stdin);
×
2736
#endif  /* HAVE_FSEEK */
2737
        while (encoder->cancel_flag && !*encoder->cancel_flag) {
×
2738
            status = sixel_tty_wait_stdin(1000000);
×
2739
            if (SIXEL_FAILED(status)) {
×
2740
                goto end;
×
2741
            }
2742
            if (status != SIXEL_OK) {
×
2743
                break;
×
2744
            }
2745
        }
2746
        if (!encoder->cancel_flag || !*encoder->cancel_flag) {
×
2747
            goto reload;
×
2748
        }
2749
    }
2750

2751
    /* the status may not be SIXEL_OK */
2752

2753
end:
262✔
2754
    sixel_encoder_unref(encoder);
417✔
2755

2756
    return status;
417✔
2757
}
2758

2759

2760
/* encode specified pixel data to SIXEL format
2761
 * output to encoder->outfd */
2762
SIXELAPI SIXELSTATUS
2763
sixel_encoder_encode_bytes(
×
2764
    sixel_encoder_t     /* in */    *encoder,
2765
    unsigned char       /* in */    *bytes,
2766
    int                 /* in */    width,
2767
    int                 /* in */    height,
2768
    int                 /* in */    pixelformat,
2769
    unsigned char       /* in */    *palette,
2770
    int                 /* in */    ncolors)
2771
{
2772
    SIXELSTATUS status = SIXEL_FALSE;
×
2773
    sixel_frame_t *frame = NULL;
×
2774

2775
    if (encoder == NULL || bytes == NULL) {
×
2776
        status = SIXEL_BAD_ARGUMENT;
×
2777
        goto end;
×
2778
    }
2779

2780
    status = sixel_frame_new(&frame, encoder->allocator);
×
2781
    if (SIXEL_FAILED(status)) {
×
2782
        goto end;
×
2783
    }
2784

2785
    status = sixel_frame_init(frame, bytes, width, height,
×
2786
                              pixelformat, palette, ncolors);
2787
    if (SIXEL_FAILED(status)) {
×
2788
        goto end;
×
2789
    }
2790

2791
    status = sixel_encoder_encode_frame(encoder, frame, NULL);
×
2792
    if (SIXEL_FAILED(status)) {
×
2793
        goto end;
×
2794
    }
2795

2796
    status = SIXEL_OK;
×
2797

2798
end:
2799
    /* we need to free the frame before exiting, but we can't use the
2800
       sixel_frame_destroy function, because that will also attempt to
2801
       free the pixels and palette, which we don't own */
2802
    if (frame != NULL && encoder->allocator != NULL) {
×
2803
        sixel_allocator_free(encoder->allocator, frame);
×
2804
        sixel_allocator_unref(encoder->allocator);
×
2805
    }
2806
    return status;
×
2807
}
2808

2809

2810
#if HAVE_TESTS
2811
static int
2812
test1(void)
×
2813
{
2814
    int nret = EXIT_FAILURE;
×
2815
    sixel_encoder_t *encoder = NULL;
×
2816

2817
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2818
#  pragma GCC diagnostic push
2819
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2820
#endif
2821
    encoder = sixel_encoder_create();
×
2822
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2823
#  pragma GCC diagnostic pop
2824
#endif
2825
    if (encoder == NULL) {
×
2826
        goto error;
×
2827
    }
2828
    sixel_encoder_ref(encoder);
×
2829
    sixel_encoder_unref(encoder);
×
2830
    nret = EXIT_SUCCESS;
×
2831

2832
error:
2833
    sixel_encoder_unref(encoder);
×
2834
    return nret;
×
2835
}
2836

2837

2838
static int
2839
test2(void)
×
2840
{
2841
    int nret = EXIT_FAILURE;
×
2842
    SIXELSTATUS status;
2843
    sixel_encoder_t *encoder = NULL;
×
2844
    sixel_frame_t *frame = NULL;
×
2845
    unsigned char *buffer;
2846
    int height = 0;
×
2847
    int is_animation = 0;
×
2848

2849
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2850
#  pragma GCC diagnostic push
2851
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2852
#endif
2853
    encoder = sixel_encoder_create();
×
2854
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2855
#  pragma GCC diagnostic pop
2856
#endif
2857
    if (encoder == NULL) {
×
2858
        goto error;
×
2859
    }
2860

2861
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2862
#  pragma GCC diagnostic push
2863
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2864
#endif
2865
    frame = sixel_frame_create();
×
2866
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2867
#  pragma GCC diagnostic pop
2868
#endif
2869
    if (encoder == NULL) {
×
2870
        goto error;
×
2871
    }
2872

2873
    buffer = (unsigned char *)sixel_allocator_malloc(encoder->allocator, 3);
×
2874
    if (buffer == NULL) {
×
2875
        goto error;
×
2876
    }
2877
    status = sixel_frame_init(frame, buffer, 1, 1,
×
2878
                              SIXEL_PIXELFORMAT_RGB888,
2879
                              NULL, 0);
2880
    if (SIXEL_FAILED(status)) {
×
2881
        goto error;
×
2882
    }
2883

2884
    if (sixel_frame_get_loop_no(frame) != 0 || sixel_frame_get_frame_no(frame) != 0) {
×
2885
        is_animation = 1;
×
2886
    }
2887

2888
    height = sixel_frame_get_height(frame);
×
2889

2890
    status = sixel_tty_scroll(sixel_write_callback, encoder->outfd, height, is_animation);
×
2891
    if (SIXEL_FAILED(status)) {
×
2892
        goto error;
×
2893
    }
2894

2895
    nret = EXIT_SUCCESS;
×
2896

2897
error:
2898
    sixel_encoder_unref(encoder);
×
2899
    sixel_frame_unref(frame);
×
2900
    return nret;
×
2901
}
2902

2903

2904
static int
2905
test3(void)
×
2906
{
2907
    int nret = EXIT_FAILURE;
×
2908
    int result;
2909

2910
    result = sixel_tty_wait_stdin(1000);
×
2911
    if (result != 0) {
×
2912
        goto error;
×
2913
    }
2914

2915
    nret = EXIT_SUCCESS;
×
2916

2917
error:
2918
    return nret;
×
2919
}
2920

2921

2922
static int
2923
test4(void)
×
2924
{
2925
    int nret = EXIT_FAILURE;
×
2926
    sixel_encoder_t *encoder = NULL;
×
2927
    SIXELSTATUS status;
2928

2929
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2930
# pragma GCC diagnostic push
2931
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2932
#endif
2933
    encoder = sixel_encoder_create();
×
2934
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2935
# pragma GCC diagnostic pop
2936
#endif
2937
    if (encoder == NULL) {
×
2938
        goto error;
×
2939
    }
2940

2941
    status = sixel_encoder_setopt(encoder,
×
2942
                                  SIXEL_OPTFLAG_LOOPMODE,
2943
                                  "force");
2944
    if (SIXEL_FAILED(status)) {
×
2945
        goto error;
×
2946
    }
2947

2948
    status = sixel_encoder_setopt(encoder,
×
2949
                                  SIXEL_OPTFLAG_PIPE_MODE,
2950
                                  "force");
2951
    if (SIXEL_FAILED(status)) {
×
2952
        goto error;
×
2953
    }
2954

2955
    nret = EXIT_SUCCESS;
×
2956

2957
error:
2958
    sixel_encoder_unref(encoder);
×
2959
    return nret;
×
2960
}
2961

2962

2963
static int
2964
test5(void)
×
2965
{
2966
    int nret = EXIT_FAILURE;
×
2967
    sixel_encoder_t *encoder = NULL;
×
2968
    sixel_allocator_t *allocator = NULL;
×
2969
    SIXELSTATUS status;
2970

2971
    status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
×
2972
    if (SIXEL_FAILED(status)) {
×
2973
        goto error;
×
2974
    }
2975

2976
    status = sixel_encoder_new(&encoder, allocator);
×
2977
    if (SIXEL_FAILED(status)) {
×
2978
        goto error;
×
2979
    }
2980

2981
    sixel_encoder_ref(encoder);
×
2982
    sixel_encoder_unref(encoder);
×
2983
    nret = EXIT_SUCCESS;
×
2984

2985
error:
2986
    sixel_encoder_unref(encoder);
×
2987
    return nret;
×
2988
}
2989

2990

2991
SIXELAPI int
2992
sixel_encoder_tests_main(void)
×
2993
{
2994
    int nret = EXIT_FAILURE;
×
2995
    size_t i;
2996
    typedef int (* testcase)(void);
2997

2998
    static testcase const testcases[] = {
2999
        test1,
3000
        test2,
3001
        test3,
3002
        test4,
3003
        test5
3004
    };
3005

3006
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
3007
        nret = testcases[i]();
×
3008
        if (nret != EXIT_SUCCESS) {
×
3009
            goto error;
×
3010
        }
3011
    }
3012

3013
    nret = EXIT_SUCCESS;
×
3014

3015
error:
3016
    return nret;
×
3017
}
3018
#endif  /* HAVE_TESTS */
3019

3020

3021
/* emacs Local Variables:      */
3022
/* emacs mode: c               */
3023
/* emacs tab-width: 4          */
3024
/* emacs indent-tabs-mode: nil */
3025
/* emacs c-basic-offset: 4     */
3026
/* emacs End:                  */
3027
/* vim: set expandtab ts=4 : */
3028
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc