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

saitoha / libsixel / 18822876255

26 Oct 2025 07:46PM UTC coverage: 46.314% (+0.2%) from 46.099%
18822876255

push

github

saitoha
loader: add new loader APIs, mark sixel_helper_load_image_file() as deprecated

5807 of 18002 branches covered (32.26%)

204 of 329 new or added lines in 3 files covered. (62.01%)

3 existing lines in 1 file now uncovered.

8520 of 18396 relevant lines covered (46.31%)

1020584.0 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
#if !defined(_POSIX_C_SOURCE)
56
# define _POSIX_C_SOURCE 200809L
57
#endif
58

59
/* STDC_HEADERS */
60
#include <stdio.h>
61
#include <stdlib.h>
62
#include <stdarg.h>
63

64
# if HAVE_STRING_H
65
#include <string.h>
66
#endif  /* HAVE_STRING_H */
67
#if HAVE_UNISTD_H
68
# include <unistd.h>
69
#elif HAVE_SYS_UNISTD_H
70
# include <sys/unistd.h>
71
#endif  /* HAVE_SYS_UNISTD_H */
72
#if HAVE_SYS_TYPES_H
73
# include <sys/types.h>
74
#endif  /* HAVE_SYS_TYPES_H */
75
#if HAVE_INTTYPES_H
76
# include <inttypes.h>
77
#endif  /* HAVE_INTTYPES_H */
78
#if HAVE_ERRNO_H
79
# include <errno.h>
80
#endif  /* HAVE_ERRNO_H */
81
#if HAVE_SYS_STAT_H
82
# include <sys/stat.h>
83
#endif  /* HAVE_SYS_STAT_H */
84
#if HAVE_SYS_TIME_H
85
# include <sys/time.h>
86
#elif HAVE_TIME_H
87
# include <time.h>
88
#endif  /* HAVE_SYS_TIME_H HAVE_TIME_H */
89
#if HAVE_SYS_IOCTL_H
90
# include <sys/ioctl.h>
91
#endif  /* HAVE_SYS_IOCTL_H */
92
#if HAVE_FCNTL_H
93
# include <fcntl.h>
94
#endif  /* HAVE_FCNTL_H */
95
#if HAVE_ERRNO_H
96
# include <errno.h>
97
#endif  /* HAVE_ERRNO_H */
98
#if HAVE_CTYPE_H
99
# include <ctype.h>
100
#endif  /* HAVE_CTYPE_H */
101
#if HAVE_LIMITS_H
102
# include <limits.h>
103
#endif  /* HAVE_LIMITS_H */
104

105
#include <sixel.h>
106
#include "loader.h"
107
#include "tty.h"
108
#include "encoder.h"
109
#include "output.h"
110
#include "dither.h"
111
#include "frame.h"
112
#include "rgblookup.h"
113

114
#if defined(_WIN32)
115

116
# include <windows.h>
117
# if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
118
#  include <io.h>
119
# endif
120
# if defined(_MSC_VER)
121
#   include <time.h>
122
# endif
123

124
/* for msvc */
125
# ifndef STDIN_FILENO
126
#  define STDIN_FILENO 0
127
# endif
128
# ifndef STDOUT_FILENO
129
#  define STDOUT_FILENO 1
130
# endif
131
# ifndef STDERR_FILENO
132
#  define STDERR_FILENO 2
133
# endif
134
# ifndef S_IRUSR
135
#  define S_IRUSR _S_IREAD
136
# endif
137
# ifndef S_IWUSR
138
#  define S_IWUSR _S_IWRITE
139
# endif
140

141
# if defined(CLOCKS_PER_SEC)
142
#  undef CLOCKS_PER_SEC
143
# endif
144
# define CLOCKS_PER_SEC 1000
145

146
# if !defined(HAVE_NANOSLEEP)
147
# define HAVE_NANOSLEEP_WIN 1
148
static int
149
nanosleep_win(
150
    struct timespec const *req,
151
    struct timespec *rem)
152
{
153
    LONGLONG nanoseconds;
154
    LARGE_INTEGER dueTime;
155
    HANDLE timer;
156

157
    if (req == NULL || req->tv_sec < 0 || req->tv_nsec < 0 ||
158
        req->tv_nsec >= 1000000000L) {
159
        errno = EINVAL;
160
        return (-1);
161
    }
162

163
    /* Convert to 100-nanosecond intervals (Windows FILETIME units) */
164
    nanoseconds = req->tv_sec * 1000000000LL + req->tv_nsec;
165
    dueTime.QuadPart = -(nanoseconds / 100); /* Negative for relative time */
166

167
    timer = CreateWaitableTimer(NULL, TRUE, NULL);
168
    if (timer == NULL) {
169
        errno = EFAULT;  /* Approximate error */
170
        return (-1);
171
    }
172

173
    if (! SetWaitableTimer(timer, &dueTime, 0, NULL, NULL, FALSE)) {
174
        (void) CloseHandle(timer);
175
        errno = EFAULT;
176
        return (-1);
177
    }
178

179
    (void) WaitForSingleObject(timer, INFINITE);
180
    (void) CloseHandle(timer);
181

182
    /* No interruption handling, so rem is unchanged */
183
    if (rem != NULL) {
184
        rem->tv_sec = 0;
185
        rem->tv_nsec = 0;
186
    }
187

188
    return (0);
189
}
190
# endif  /* HAVE_NANOSLEEP */
191

192
# if !defined(HAVE_CLOCK)
193
# define HAVE_CLOCK_WIN 1
194
static sixel_clock_t
195
clock_win(void)
196
{
197
    FILETIME ct, et, kt, ut;
198
    ULARGE_INTEGER u, k;
199

200
    if (! GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
201
        return (sixel_clock_t)(-1);
202
    }
203
    u.LowPart = ut.dwLowDateTime; u.HighPart = ut.dwHighDateTime;
204
    k.LowPart = kt.dwLowDateTime; k.HighPart = kt.dwHighDateTime;
205
    /* 100ns -> ms */
206
    return (sixel_clock_t)((u.QuadPart + k.QuadPart) / 10000ULL);
207
}
208
# endif  /* HAVE_CLOCK */
209

210
#endif /* _WIN32 */
211

212

213
static char *
214
arg_strdup(
60✔
215
    char const          /* in */ *s,          /* source buffer */
216
    sixel_allocator_t   /* in */ *allocator)  /* allocator object for
217
                                                 destination buffer */
218
{
219
    char *p;
220
    size_t len;
221

222
    len = strlen(s);
60✔
223

224
    p = (char *)sixel_allocator_malloc(allocator, len + 1);
60✔
225
    if (p) {
60!
226
#if HAVE_STRCPY_S
227
        (void) strcpy_s(p, (rsize_t)len, s);
228
#else
229
        (void) strcpy(p, s);
60✔
230
#endif  /* HAVE_STRCPY_S */
231
    }
20✔
232
    return p;
60✔
233
}
234

235

236
/* An clone function of XColorSpec() of xlib */
237
static SIXELSTATUS
238
sixel_parse_x_colorspec(
45✔
239
    unsigned char       /* out */ **bgcolor,     /* destination buffer */
240
    char const          /* in */  *s,            /* source buffer */
241
    sixel_allocator_t   /* in */  *allocator)    /* allocator object for
242
                                                    destination buffer */
243
{
244
    SIXELSTATUS status = SIXEL_FALSE;
45✔
245
    char *p;
246
    unsigned char components[3];
247
    int component_index = 0;
45✔
248
    unsigned long v;
249
    char *endptr;
250
    char *buf = NULL;
45✔
251
    struct color const *pcolor;
252

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

384
    status = SIXEL_OK;
24✔
385

386
end:
30✔
387
    sixel_allocator_free(allocator, buf);
45✔
388

389
    return status;
45✔
390
}
391

392

393
/* generic writer function for passing to sixel_output_new() */
394
static int
395
sixel_write_callback(char *data, int size, void *priv)
4,100✔
396
{
397
    int result;
398

399
#if HAVE__WRITE
400
    result = _write(*(int *)priv, data, (size_t)size);
401
#elif defined(__MINGW64__)
402
    result = write(*(int *)priv, data, (unsigned int)size);
403
#else
404
    result = write(*(int *)priv, data, (size_t)size);
4,100✔
405
#endif
406

407
    return result;
4,100✔
408
}
409

410

411
/* the writer function with hex-encoding for passing to sixel_output_new() */
412
static int
413
sixel_hex_write_callback(
73✔
414
    char    /* in */ *data,
415
    int     /* in */ size,
416
    void    /* in */ *priv)
417
{
418
    char hex[SIXEL_OUTPUT_PACKET_SIZE * 2];
419
    int i;
420
    int j;
421
    int result;
422

423
    for (i = j = 0; i < size; ++i, ++j) {
635,609✔
424
        hex[j] = (data[i] >> 4) & 0xf;
635,536✔
425
        hex[j] += (hex[j] < 10 ? '0': ('a' - 10));
635,536!
426
        hex[++j] = data[i] & 0xf;
635,536✔
427
        hex[j] += (hex[j] < 10 ? '0': ('a' - 10));
635,536✔
428
    }
168,020✔
429

430
#if HAVE__WRITE
431
    result = _write(*(int *)priv, hex, (unsigned int)(size * 2));
432
#elif defined(__MINGW64__)
433
    result = write(*(int *)priv, hex, (unsigned int)(size * 2));
434
#else
435
    result = write(*(int *)priv, hex, (size_t)(size * 2));
73✔
436
#endif
437

438
    return result;
73✔
439
}
440

441
static SIXELSTATUS
442
sixel_encoder_ensure_cell_size(sixel_encoder_t *encoder)
×
443
{
444
#if defined(TIOCGWINSZ)
445
    struct winsize ws;
446
    int result;
447
    int fd = 0;
×
448

449
    if (encoder->cell_width > 0 && encoder->cell_height > 0) {
×
450
        return SIXEL_OK;
×
451
    }
452

453
#if HAVE__OPEN
454
    fd = _open("/dev/tty", O_RDONLY);
455
#else
456
    fd = open("/dev/tty", O_RDONLY);
×
457
#endif  /* #if HAVE__OPEN */
458
    if (fd >= 0) {
×
459
        result = ioctl(fd, TIOCGWINSZ, &ws);
×
460
        close(fd);
×
461
    } else {
462
        sixel_helper_set_additional_message(
×
463
            "failed to open /dev/tty");
464
        return (SIXEL_LIBC_ERROR | (errno & 0xff));
×
465
    }
466
    if (result != 0) {
×
467
        sixel_helper_set_additional_message(
×
468
            "failed to query terminal geometry with ioctl().");
469
        return (SIXEL_LIBC_ERROR | (errno & 0xff));
×
470
    }
471

472
    if (ws.ws_col <= 0 || ws.ws_row <= 0 ||
×
473
        ws.ws_xpixel <= ws.ws_col || ws.ws_ypixel <= ws.ws_row) {
×
474
        sixel_helper_set_additional_message(
×
475
            "terminal does not report pixel cell size for drcs option.");
476
        return SIXEL_BAD_ARGUMENT;
×
477
    }
478

479
    encoder->cell_width = ws.ws_xpixel / ws.ws_col;
×
480
    encoder->cell_height = ws.ws_ypixel / ws.ws_row;
×
481
    if (encoder->cell_width <= 0 || encoder->cell_height <= 0) {
×
482
        sixel_helper_set_additional_message(
×
483
            "terminal cell size reported zero via ioctl().");
484
        return SIXEL_BAD_ARGUMENT;
×
485
    }
486

487
    return SIXEL_OK;
×
488
#else
489
    (void) encoder;
490
    sixel_helper_set_additional_message(
491
        "drcs option is not supported on this platform.");
492
    return SIXEL_NOT_IMPLEMENTED;
493
#endif
494
}
495

496

497
/* returns monochrome dithering context object */
498
static SIXELSTATUS
499
sixel_prepare_monochrome_palette(
12✔
500
    sixel_dither_t  /* out */ **dither,
501
     int            /* in */  finvert)
502
{
503
    SIXELSTATUS status = SIXEL_FALSE;
12✔
504

505
    if (finvert) {
12✔
506
        *dither = sixel_dither_get(SIXEL_BUILTIN_MONO_LIGHT);
3✔
507
    } else {
1✔
508
        *dither = sixel_dither_get(SIXEL_BUILTIN_MONO_DARK);
9✔
509
    }
510
    if (*dither == NULL) {
12!
511
        sixel_helper_set_additional_message(
×
512
            "sixel_prepare_monochrome_palette: sixel_dither_get() failed.");
513
        status = SIXEL_RUNTIME_ERROR;
×
514
        goto end;
×
515
    }
516

517
    status = SIXEL_OK;
12✔
518

519
end:
8✔
520
    return status;
12✔
521
}
522

523

524
/* returns dithering context object with specified builtin palette */
525
typedef struct palette_conversion {
526
    unsigned char *original;
527
    unsigned char *copy;
528
    size_t size;
529
    int convert_inplace;
530
    int converted;
531
    int frame_colorspace;
532
} palette_conversion_t;
533

534
static SIXELSTATUS
535
sixel_encoder_convert_palette(sixel_encoder_t *encoder,
504✔
536
                              sixel_output_t *output,
537
                              sixel_dither_t *dither,
538
                              int frame_colorspace,
539
                              int pixelformat,
540
                              palette_conversion_t *ctx)
541
{
542
    SIXELSTATUS status = SIXEL_OK;
504✔
543
    unsigned char *palette;
544
    int palette_colors;
545

546
    ctx->original = NULL;
504✔
547
    ctx->copy = NULL;
504✔
548
    ctx->size = 0;
504✔
549
    ctx->convert_inplace = 0;
504✔
550
    ctx->converted = 0;
504✔
551
    ctx->frame_colorspace = frame_colorspace;
504✔
552

553
    palette = sixel_dither_get_palette(dither);
504✔
554
    palette_colors = sixel_dither_get_num_of_palette_colors(dither);
504✔
555
    ctx->original = palette;
504✔
556

557
    if (palette == NULL || palette_colors <= 0 ||
504!
558
            frame_colorspace == output->colorspace) {
504!
559
        return SIXEL_OK;
504✔
560
    }
561

562
    ctx->size = (size_t)palette_colors * 3;
×
563

564
    output->pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
565
    output->source_colorspace = frame_colorspace;
×
566

567
    if (palette != (unsigned char *)(dither + 1)) {
×
568
        ctx->copy = (unsigned char *)sixel_allocator_malloc(
×
569
            encoder->allocator, ctx->size);
570
        if (ctx->copy == NULL) {
×
571
            sixel_helper_set_additional_message(
×
572
                "sixel_encoder_convert_palette: "
573
                "sixel_allocator_malloc() failed.");
574
            status = SIXEL_BAD_ALLOCATION;
×
575
            goto end;
×
576
        }
577
        memcpy(ctx->copy, palette, ctx->size);
×
578
        dither->palette = ctx->copy;
×
579
    } else {
580
        ctx->convert_inplace = 1;
×
581
    }
582

583
    status = sixel_output_convert_colorspace(output,
×
584
                                             dither->palette,
585
                                             ctx->size);
586
    if (SIXEL_FAILED(status)) {
×
587
        goto end;
×
588
    }
589
    ctx->converted = 1;
×
590

591
end:
592
    output->pixelformat = pixelformat;
×
593
    output->source_colorspace = frame_colorspace;
×
594

595
    return status;
×
596
}
174✔
597

598
static void
599
sixel_encoder_restore_palette(sixel_encoder_t *encoder,
504✔
600
                              sixel_dither_t *dither,
601
                              palette_conversion_t *ctx)
602
{
603
    if (ctx->copy) {
504!
604
        dither->palette = ctx->original;
×
605
        sixel_allocator_free(encoder->allocator, ctx->copy);
×
606
        ctx->copy = NULL;
×
607
    } else if (ctx->convert_inplace && ctx->converted &&
504!
608
               ctx->original && ctx->size > 0) {
×
609
        (void)sixel_helper_convert_colorspace(ctx->original,
×
610
                                              ctx->size,
611
                                              SIXEL_PIXELFORMAT_RGB888,
612
                                              SIXEL_COLORSPACE_GAMMA,
613
                                              ctx->frame_colorspace);
614
    }
615
}
504✔
616

617
static SIXELSTATUS
618
sixel_prepare_builtin_palette(
27✔
619
    sixel_dither_t /* out */ **dither,
620
    int            /* in */  builtin_palette)
621
{
622
    SIXELSTATUS status = SIXEL_FALSE;
27✔
623

624
    *dither = sixel_dither_get(builtin_palette);
27✔
625
    if (*dither == NULL) {
27!
626
        sixel_helper_set_additional_message(
×
627
            "sixel_prepare_builtin_palette: sixel_dither_get() failed.");
628
        status = SIXEL_RUNTIME_ERROR;
×
629
        goto end;
×
630
    }
631

632
    status = SIXEL_OK;
27✔
633

634
end:
18✔
635
    return status;
27✔
636
}
637

638
static int
639
sixel_encoder_thumbnail_hint(sixel_encoder_t *encoder)
443✔
640
{
641
    int width_hint;
642
    int height_hint;
643
    long base;
644
    long size;
645

646
    width_hint = 0;
443✔
647
    height_hint = 0;
443✔
648
    base = 0;
443✔
649
    size = 0;
443✔
650

651
    if (encoder == NULL) {
443!
652
        return 0;
×
653
    }
654

655
    width_hint = encoder->pixelwidth;
443✔
656
    height_hint = encoder->pixelheight;
443✔
657

658
    /* Request extra resolution for downscaling to preserve detail. */
659
    if (width_hint > 0 && height_hint > 0) {
443✔
660
        /* Follow the CLI rule: double the larger axis before doubling
661
         * again for the final request size. */
662
        if (width_hint >= height_hint) {
9!
663
            base = (long)width_hint;
9✔
664
        } else {
3✔
665
            base = (long)height_hint;
×
666
        }
667
        base *= 2L;
9✔
668
    } else if (width_hint > 0) {
437✔
669
        base = (long)width_hint;
48✔
670
    } else if (height_hint > 0) {
402✔
671
        base = (long)height_hint;
36✔
672
    } else {
12✔
673
        return 0;
350✔
674
    }
675

676
    size = base * 2L;
93✔
677
    if (size > (long)INT_MAX) {
93!
678
        size = (long)INT_MAX;
×
679
    }
680
    if (size < 1L) {
93!
681
        size = 1L;
×
682
    }
683

684
    return (int)size;
93✔
685
}
149✔
686

687

688
typedef struct sixel_callback_context_for_mapfile {
689
    int reqcolors;
690
    sixel_dither_t *dither;
691
    sixel_allocator_t *allocator;
692
    int working_colorspace;
693
    int lut_policy;
694
} sixel_callback_context_for_mapfile_t;
695

696

697
/* callback function for sixel_helper_load_image_file() */
698
static SIXELSTATUS
699
load_image_callback_for_palette(
21✔
700
    sixel_frame_t   /* in */    *frame, /* frame object from image loader */
701
    void            /* in */    *data)  /* private data */
702
{
703
    SIXELSTATUS status = SIXEL_FALSE;
21✔
704
    sixel_callback_context_for_mapfile_t *callback_context;
705

706
    /* get callback context object from the private data */
707
    callback_context = (sixel_callback_context_for_mapfile_t *)data;
21✔
708

709
    status = sixel_frame_ensure_colorspace(frame,
28✔
710
                                           callback_context->working_colorspace);
7✔
711
    if (SIXEL_FAILED(status)) {
21!
712
        goto end;
×
713
    }
714

715
    switch (sixel_frame_get_pixelformat(frame)) {
21!
716
    case SIXEL_PIXELFORMAT_PAL1:
2✔
717
    case SIXEL_PIXELFORMAT_PAL2:
718
    case SIXEL_PIXELFORMAT_PAL4:
719
    case SIXEL_PIXELFORMAT_PAL8:
720
        if (sixel_frame_get_palette(frame) == NULL) {
3!
721
            status = SIXEL_LOGIC_ERROR;
×
722
            goto end;
×
723
        }
724
        /* create new dither object */
725
        status = sixel_dither_new(
3✔
726
            &callback_context->dither,
1✔
727
            sixel_frame_get_ncolors(frame),
1✔
728
            callback_context->allocator);
1✔
729
        if (SIXEL_FAILED(status)) {
3!
730
            goto end;
×
731
        }
732

733
        sixel_dither_set_lut_policy(callback_context->dither,
4✔
734
                                    callback_context->lut_policy);
1✔
735

736
        /* use palette which is extracted from the image */
737
        sixel_dither_set_palette(callback_context->dither,
4✔
738
                                 sixel_frame_get_palette(frame));
1✔
739
        /* success */
740
        status = SIXEL_OK;
3✔
741
        break;
3✔
742
    case SIXEL_PIXELFORMAT_G1:
743
        /* use 1bpp grayscale builtin palette */
744
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G1);
×
745
        /* success */
746
        status = SIXEL_OK;
×
747
        break;
×
748
    case SIXEL_PIXELFORMAT_G2:
749
        /* use 2bpp grayscale builtin palette */
750
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G1);
×
751
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G2);
×
752
        /* success */
753
        status = SIXEL_OK;
×
754
        break;
×
755
    case SIXEL_PIXELFORMAT_G4:
756
        /* use 4bpp grayscale builtin palette */
757
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G4);
×
758
        /* success */
759
        status = SIXEL_OK;
×
760
        break;
×
761
    case SIXEL_PIXELFORMAT_G8:
762
        /* use 8bpp grayscale builtin palette */
763
        callback_context->dither = sixel_dither_get(SIXEL_BUILTIN_G8);
×
764
        /* success */
765
        status = SIXEL_OK;
×
766
        break;
×
767
    default:
12✔
768
        /* create new dither object */
769
        status = sixel_dither_new(
18✔
770
            &callback_context->dither,
6✔
771
            callback_context->reqcolors,
6✔
772
            callback_context->allocator);
6✔
773
        if (SIXEL_FAILED(status)) {
18!
774
            goto end;
×
775
        }
776

777
        sixel_dither_set_lut_policy(callback_context->dither,
24✔
778
                                    callback_context->lut_policy);
6✔
779

780
        /* create adaptive palette from given frame object */
781
        status = sixel_dither_initialize(callback_context->dither,
24✔
782
                                         sixel_frame_get_pixels(frame),
6✔
783
                                         sixel_frame_get_width(frame),
6✔
784
                                         sixel_frame_get_height(frame),
6✔
785
                                         sixel_frame_get_pixelformat(frame),
6✔
786
                                         SIXEL_LARGE_NORM,
787
                                         SIXEL_REP_CENTER_BOX,
788
                                         SIXEL_QUALITY_HIGH);
789
        if (SIXEL_FAILED(status)) {
18!
790
            sixel_dither_unref(callback_context->dither);
×
791
            goto end;
×
792
        }
793

794
        /* success */
795
        status = SIXEL_OK;
18✔
796

797
        break;
18✔
798
    }
7✔
799

800
end:
14✔
801
    return status;
21✔
802
}
803

804

805
/* create palette from specified map file */
806
static SIXELSTATUS
807
sixel_prepare_specified_palette(
26✔
808
    sixel_dither_t  /* out */   **dither,
809
    sixel_encoder_t /* in */    *encoder)
810
{
811
    SIXELSTATUS status = SIXEL_FALSE;
26✔
812
    sixel_callback_context_for_mapfile_t callback_context;
813
    sixel_loader_t *loader;
814
    int fstatic;
815
    int fuse_palette;
816
    int reqcolors;
817
    int loop_override;
818

819
    callback_context.reqcolors = encoder->reqcolors;
26✔
820
    callback_context.dither = NULL;
26✔
821
    callback_context.allocator = encoder->allocator;
26✔
822
    callback_context.working_colorspace = encoder->working_colorspace;
26✔
823
    callback_context.lut_policy = encoder->lut_policy;
26✔
824

825
    loader = NULL;
26✔
826
    fstatic = 1;
26✔
827
    fuse_palette = 1;
26✔
828
    reqcolors = SIXEL_PALETTE_MAX;
26✔
829
    loop_override = SIXEL_LOOP_DISABLE;
26✔
830

831
    sixel_helper_set_loader_trace(encoder->verbose);
26✔
832
    sixel_helper_set_thumbnail_size_hint(
26✔
833
        sixel_encoder_thumbnail_hint(encoder));
10✔
834
    status = sixel_loader_new(&loader, encoder->allocator);
26✔
835
    if (SIXEL_FAILED(status)) {
26!
NEW
836
        goto end_loader;
×
837
    }
838

839
    status = sixel_loader_setopt(loader,
26✔
840
                                 SIXEL_LOADER_OPTION_REQUIRE_STATIC,
841
                                 &fstatic);
842
    if (SIXEL_FAILED(status)) {
26!
NEW
843
        goto end_loader;
×
844
    }
845

846
    status = sixel_loader_setopt(loader,
26✔
847
                                 SIXEL_LOADER_OPTION_USE_PALETTE,
848
                                 &fuse_palette);
849
    if (SIXEL_FAILED(status)) {
26!
NEW
850
        goto end_loader;
×
851
    }
852

853
    status = sixel_loader_setopt(loader,
26✔
854
                                 SIXEL_LOADER_OPTION_REQCOLORS,
855
                                 &reqcolors);
856
    if (SIXEL_FAILED(status)) {
26!
NEW
857
        goto end_loader;
×
858
    }
859

860
    status = sixel_loader_setopt(loader,
36✔
861
                                 SIXEL_LOADER_OPTION_BGCOLOR,
862
                                 encoder->bgcolor);
26✔
863
    if (SIXEL_FAILED(status)) {
26!
NEW
864
        goto end_loader;
×
865
    }
866

867
    status = sixel_loader_setopt(loader,
26✔
868
                                 SIXEL_LOADER_OPTION_LOOP_CONTROL,
869
                                 &loop_override);
870
    if (SIXEL_FAILED(status)) {
26!
NEW
871
        goto end_loader;
×
872
    }
873

874
    status = sixel_loader_setopt(loader,
36✔
875
                                 SIXEL_LOADER_OPTION_INSECURE,
876
                                 &encoder->finsecure);
26✔
877
    if (SIXEL_FAILED(status)) {
26!
NEW
878
        goto end_loader;
×
879
    }
880

881
    status = sixel_loader_setopt(loader,
36✔
882
                                 SIXEL_LOADER_OPTION_CANCEL_FLAG,
883
                                 encoder->cancel_flag);
26✔
884
    if (SIXEL_FAILED(status)) {
26!
NEW
885
        goto end_loader;
×
886
    }
887

888
    status = sixel_loader_setopt(loader,
36✔
889
                                 SIXEL_LOADER_OPTION_LOADER_ORDER,
890
                                 encoder->loader_order);
26✔
891
    if (SIXEL_FAILED(status)) {
26!
NEW
892
        goto end_loader;
×
893
    }
894

895
    status = sixel_loader_setopt(loader,
26✔
896
                                 SIXEL_LOADER_OPTION_CONTEXT,
897
                                 &callback_context);
898
    if (SIXEL_FAILED(status)) {
26!
NEW
899
        goto end_loader;
×
900
    }
901

902
    status = sixel_loader_load_file(loader,
36✔
903
                                    encoder->mapfile,
26✔
904
                                    load_image_callback_for_palette);
905
    if (status != SIXEL_OK) {
26✔
906
        goto end_loader;
5✔
907
    }
908

909
end_loader:
14✔
910
    sixel_loader_unref(loader);
26✔
911

912
    if (status != SIXEL_OK) {
26✔
913
        return status;
5✔
914
    }
915

916
    if (! callback_context.dither) {
21!
917
        sixel_helper_set_additional_message(
×
918
            "sixel_prepare_specified_palette() failed.\n"
919
            "reason: mapfile is empty.");
920
        return SIXEL_BAD_INPUT;
×
921
    }
922

923
    *dither = callback_context.dither;
21✔
924

925
    return status;
21✔
926
}
10✔
927

928

929
/* create dither object from a frame */
930
static SIXELSTATUS
931
sixel_encoder_prepare_palette(
509✔
932
    sixel_encoder_t *encoder,  /* encoder object */
933
    sixel_frame_t   *frame,    /* input frame object */
934
    sixel_dither_t  **dither)  /* dither object to be created from the frame */
935
{
936
    SIXELSTATUS status = SIXEL_FALSE;
509✔
937
    int histogram_colors;
938

939
    switch (encoder->color_option) {
509!
940
    case SIXEL_COLOR_OPTION_HIGHCOLOR:
24✔
941
        if (encoder->dither_cache) {
36!
942
            *dither = encoder->dither_cache;
×
943
            status = SIXEL_OK;
×
944
        } else {
945
            status = sixel_dither_new(dither, (-1), encoder->allocator);
36✔
946
            sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
36✔
947
        }
948
        goto end;
36✔
949
    case SIXEL_COLOR_OPTION_MONOCHROME:
8✔
950
        if (encoder->dither_cache) {
12!
951
            *dither = encoder->dither_cache;
×
952
            status = SIXEL_OK;
×
953
        } else {
954
            status = sixel_prepare_monochrome_palette(dither, encoder->finvert);
12✔
955
        }
956
        goto end;
12✔
957
    case SIXEL_COLOR_OPTION_MAPFILE:
16✔
958
        if (encoder->dither_cache) {
26!
959
            *dither = encoder->dither_cache;
×
960
            status = SIXEL_OK;
×
961
        } else {
962
            status = sixel_prepare_specified_palette(dither, encoder);
26✔
963
        }
964
        goto end;
26✔
965
    case SIXEL_COLOR_OPTION_BUILTIN:
18✔
966
        if (encoder->dither_cache) {
27!
967
            *dither = encoder->dither_cache;
×
968
            status = SIXEL_OK;
×
969
        } else {
970
            status = sixel_prepare_builtin_palette(dither, encoder->builtin_palette);
27✔
971
        }
972
        goto end;
27✔
973
    case SIXEL_COLOR_OPTION_DEFAULT:
408✔
974
    default:
975
        break;
408✔
976
    }
977

978
    if (sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_PALETTE) {
408✔
979
        if (!sixel_frame_get_palette(frame)) {
191!
980
            status = SIXEL_LOGIC_ERROR;
×
981
            goto end;
×
982
        }
983
        status = sixel_dither_new(dither, sixel_frame_get_ncolors(frame),
230✔
984
                                  encoder->allocator);
39✔
985
        if (SIXEL_FAILED(status)) {
191!
986
            goto end;
×
987
        }
988
        sixel_dither_set_palette(*dither, sixel_frame_get_palette(frame));
191✔
989
        sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
191✔
990
        if (sixel_frame_get_transparent(frame) != (-1)) {
191!
991
            sixel_dither_set_transparent(*dither, sixel_frame_get_transparent(frame));
×
992
        }
993
        if (*dither && encoder->dither_cache) {
191!
994
            sixel_dither_unref(encoder->dither_cache);
×
995
        }
996
        goto end;
191✔
997
    }
998

999
    if (sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_GRAYSCALE) {
217!
1000
        switch (sixel_frame_get_pixelformat(frame)) {
×
1001
        case SIXEL_PIXELFORMAT_G1:
1002
            *dither = sixel_dither_get(SIXEL_BUILTIN_G1);
×
1003
            break;
×
1004
        case SIXEL_PIXELFORMAT_G2:
1005
            *dither = sixel_dither_get(SIXEL_BUILTIN_G2);
×
1006
            break;
×
1007
        case SIXEL_PIXELFORMAT_G4:
1008
            *dither = sixel_dither_get(SIXEL_BUILTIN_G4);
×
1009
            break;
×
1010
        case SIXEL_PIXELFORMAT_G8:
1011
            *dither = sixel_dither_get(SIXEL_BUILTIN_G8);
×
1012
            break;
×
1013
        default:
1014
            *dither = NULL;
×
1015
            status = SIXEL_LOGIC_ERROR;
×
1016
            goto end;
×
1017
        }
1018
        if (*dither && encoder->dither_cache) {
×
1019
            sixel_dither_unref(encoder->dither_cache);
×
1020
        }
1021
        sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
×
1022
        status = SIXEL_OK;
×
1023
        goto end;
×
1024
    }
1025

1026
    if (encoder->dither_cache) {
217!
1027
        sixel_dither_unref(encoder->dither_cache);
×
1028
    }
1029
    status = sixel_dither_new(dither, encoder->reqcolors, encoder->allocator);
217✔
1030
    if (SIXEL_FAILED(status)) {
217!
1031
        goto end;
×
1032
    }
1033

1034
    sixel_dither_set_lut_policy(*dither, encoder->lut_policy);
217✔
1035

1036
    status = sixel_dither_initialize(*dither,
320✔
1037
                                     sixel_frame_get_pixels(frame),
103✔
1038
                                     sixel_frame_get_width(frame),
103✔
1039
                                     sixel_frame_get_height(frame),
103✔
1040
                                     sixel_frame_get_pixelformat(frame),
103✔
1041
                                     encoder->method_for_largest,
103✔
1042
                                     encoder->method_for_rep,
103✔
1043
                                     encoder->quality_mode);
103✔
1044
    if (SIXEL_FAILED(status)) {
217!
1045
        sixel_dither_unref(*dither);
×
1046
        goto end;
×
1047
    }
1048

1049
    histogram_colors = sixel_dither_get_num_of_histogram_colors(*dither);
217✔
1050
    if (histogram_colors <= encoder->reqcolors) {
217✔
1051
        encoder->method_for_diffuse = SIXEL_DIFFUSE_NONE;
172✔
1052
    }
88✔
1053
    sixel_dither_set_pixelformat(*dither, sixel_frame_get_pixelformat(frame));
217✔
1054

1055
    status = SIXEL_OK;
217✔
1056

1057
end:
332✔
1058
    if (SIXEL_SUCCEEDED(status) && dither != NULL && *dither != NULL) {
509!
1059
        sixel_dither_set_lut_policy(*dither, encoder->lut_policy);
504✔
1060
        /* pass down the user's demand for an exact palette size */
1061
        (*dither)->force_palette = encoder->force_palette;
504✔
1062
    }
174✔
1063
    return status;
509✔
1064
}
1065

1066

1067
/* resize a frame with settings of specified encoder object */
1068
static SIXELSTATUS
1069
sixel_encoder_do_resize(
516✔
1070
    sixel_encoder_t /* in */    *encoder,   /* encoder object */
1071
    sixel_frame_t   /* in */    *frame)     /* frame object to be resized */
1072
{
1073
    SIXELSTATUS status = SIXEL_FALSE;
516✔
1074
    int src_width;
1075
    int src_height;
1076
    int dst_width;
1077
    int dst_height;
1078

1079
    /* get frame width and height */
1080
    src_width = sixel_frame_get_width(frame);
516✔
1081
    src_height = sixel_frame_get_height(frame);
516✔
1082

1083
    if (src_width < 1) {
516✔
1084
         sixel_helper_set_additional_message(
6✔
1085
             "sixel_encoder_do_resize: "
1086
             "detected a frame with a non-positive width.");
1087
        return SIXEL_BAD_ARGUMENT;
6✔
1088
    }
1089

1090
    if (src_height < 1) {
510!
1091
         sixel_helper_set_additional_message(
×
1092
             "sixel_encoder_do_resize: "
1093
             "detected a frame with a non-positive height.");
1094
        return SIXEL_BAD_ARGUMENT;
×
1095
    }
1096

1097
    /* settings around scaling */
1098
    dst_width = encoder->pixelwidth;    /* may be -1 (default) */
510✔
1099
    dst_height = encoder->pixelheight;  /* may be -1 (default) */
510✔
1100

1101
    /* if the encoder has percentwidth or percentheight property,
1102
       convert them to pixelwidth / pixelheight */
1103
    if (encoder->percentwidth > 0) {
510✔
1104
        dst_width = src_width * encoder->percentwidth / 100;
12✔
1105
    }
4✔
1106
    if (encoder->percentheight > 0) {
510✔
1107
        dst_height = src_height * encoder->percentheight / 100;
9✔
1108
    }
3✔
1109

1110
    /* if only either width or height is set, set also the other
1111
       to retain frame aspect ratio */
1112
    if (dst_width > 0 && dst_height <= 0) {
510✔
1113
        dst_height = src_height * dst_width / src_width;
42✔
1114
    }
14✔
1115
    if (dst_height > 0 && dst_width <= 0) {
510✔
1116
        dst_width = src_width * dst_height / src_height;
33✔
1117
    }
11✔
1118

1119
    /* do resize */
1120
    if (dst_width > 0 && dst_height > 0) {
510!
1121
        status = sixel_frame_resize(frame, dst_width, dst_height,
124✔
1122
                                    encoder->method_for_resampling);
31✔
1123
        if (SIXEL_FAILED(status)) {
93!
1124
            goto end;
×
1125
        }
1126
    }
31✔
1127

1128
    /* success */
1129
    status = SIXEL_OK;
510✔
1130

1131
end:
332✔
1132
    return status;
510✔
1133
}
180✔
1134

1135

1136
/* clip a frame with settings of specified encoder object */
1137
static SIXELSTATUS
1138
sixel_encoder_do_clip(
512✔
1139
    sixel_encoder_t /* in */    *encoder,   /* encoder object */
1140
    sixel_frame_t   /* in */    *frame)     /* frame object to be resized */
1141
{
1142
    SIXELSTATUS status = SIXEL_FALSE;
512✔
1143
    int src_width;
1144
    int src_height;
1145
    int clip_x;
1146
    int clip_y;
1147
    int clip_w;
1148
    int clip_h;
1149

1150
    /* get frame width and height */
1151
    src_width = sixel_frame_get_width(frame);
512✔
1152
    src_height = sixel_frame_get_height(frame);
512✔
1153

1154
    /* settings around clipping */
1155
    clip_x = encoder->clipx;
512✔
1156
    clip_y = encoder->clipy;
512✔
1157
    clip_w = encoder->clipwidth;
512✔
1158
    clip_h = encoder->clipheight;
512✔
1159

1160
    /* adjust clipping width with comparing it to frame width */
1161
    if (clip_w + clip_x > src_width) {
512✔
1162
        if (clip_x > src_width) {
7✔
1163
            clip_w = 0;
3✔
1164
        } else {
1✔
1165
            clip_w = src_width - clip_x;
4✔
1166
        }
1167
    }
3✔
1168

1169
    /* adjust clipping height with comparing it to frame height */
1170
    if (clip_h + clip_y > src_height) {
512✔
1171
        if (clip_y > src_height) {
6✔
1172
            clip_h = 0;
3✔
1173
        } else {
1✔
1174
            clip_h = src_height - clip_y;
3✔
1175
        }
1176
    }
2✔
1177

1178
    /* do clipping */
1179
    if (clip_w > 0 && clip_h > 0) {
512!
1180
        status = sixel_frame_clip(frame, clip_x, clip_y, clip_w, clip_h);
15✔
1181
        if (SIXEL_FAILED(status)) {
15!
1182
            goto end;
3✔
1183
        }
1184
    }
4✔
1185

1186
    /* success */
1187
    status = SIXEL_OK;
509✔
1188

1189
end:
332✔
1190
    return status;
512✔
1191
}
1192

1193

1194
static void
1195
sixel_debug_print_palette(
3✔
1196
    sixel_dither_t /* in */ *dither /* dithering object */
1197
)
1198
{
1199
    unsigned char *palette;
1200
    int i;
1201

1202
    palette = sixel_dither_get_palette(dither);
3✔
1203
    fprintf(stderr, "palette:\n");
3✔
1204
    for (i = 0; i < sixel_dither_get_num_of_palette_colors(dither); ++i) {
51✔
1205
        fprintf(stderr, "%d: #%02x%02x%02x\n", i,
64✔
1206
                palette[i * 3 + 0],
48✔
1207
                palette[i * 3 + 1],
48✔
1208
                palette[i * 3 + 2]);
48✔
1209
    }
16✔
1210
}
3✔
1211

1212

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

1239
    memset(&palette_ctx, 0, sizeof(palette_ctx));
411✔
1240
#if defined(HAVE_CLOCK) || defined(HAVE_CLOCK_WIN)
1241
    sixel_clock_t last_clock;
1242
#endif
1243

1244
    if (encoder == NULL) {
411!
1245
        sixel_helper_set_additional_message(
×
1246
            "sixel_encoder_output_without_macro: encoder object is null.");
1247
        status = SIXEL_BAD_ARGUMENT;
×
1248
        goto end;
×
1249
    }
1250

1251
    if (encoder->color_option == SIXEL_COLOR_OPTION_DEFAULT) {
411✔
1252
        if (encoder->force_palette) {
315!
1253
            /* keep every slot when the user forced the palette size */
1254
            sixel_dither_set_optimize_palette(dither, 0);
×
1255
        } else {
1256
            sixel_dither_set_optimize_palette(dither, 1);
315✔
1257
        }
1258
    }
105✔
1259

1260
    pixelformat = sixel_frame_get_pixelformat(frame);
411✔
1261
    frame_colorspace = sixel_frame_get_colorspace(frame);
411✔
1262
    output->pixelformat = pixelformat;
411✔
1263
    output->source_colorspace = frame_colorspace;
411✔
1264
    output->colorspace = encoder->output_colorspace;
411✔
1265
    sixel_dither_set_pixelformat(dither, pixelformat);
411✔
1266
    depth = sixel_helper_compute_depth(pixelformat);
411✔
1267
    if (depth < 0) {
411!
1268
        status = SIXEL_LOGIC_ERROR;
×
1269
        nwrite = sprintf(message,
×
1270
                         "sixel_encoder_output_without_macro: "
1271
                         "sixel_helper_compute_depth(%08x) failed.",
1272
                         pixelformat);
1273
        if (nwrite > 0) {
×
1274
            sixel_helper_set_additional_message(message);
×
1275
        }
1276
        goto end;
×
1277
    }
1278

1279
    width = sixel_frame_get_width(frame);
411✔
1280
    height = sixel_frame_get_height(frame);
411✔
1281
    size = (size_t)(width * height * depth);
411✔
1282
    p = (unsigned char *)sixel_allocator_malloc(encoder->allocator, size);
411✔
1283
    if (p == NULL) {
411!
1284
        sixel_helper_set_additional_message(
×
1285
            "sixel_encoder_output_without_macro: sixel_allocator_malloc() failed.");
1286
        status = SIXEL_BAD_ALLOCATION;
×
1287
        goto end;
×
1288
    }
1289
#if defined(HAVE_CLOCK)
1290
    if (output->last_clock == 0) {
411!
1291
        output->last_clock = clock();
411✔
1292
    }
137✔
1293
#elif defined(HAVE_CLOCK_WIN)
1294
    if (output->last_clock == 0) {
1295
        output->last_clock = clock_win();
1296
    }
1297
#endif
1298
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1299
    delay = sixel_frame_get_delay(frame);
411✔
1300
    if (delay > 0 && !encoder->fignore_delay) {
411✔
1301
# if defined(HAVE_CLOCK)
1302
        last_clock = clock();
76✔
1303
/* https://stackoverflow.com/questions/16697005/clock-and-clocks-per-sec-on-osx-10-7 */
1304
#  if defined(__APPLE__)
1305
        dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
148✔
1306
                          / 100000);
74✔
1307
#  else
1308
        dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
2✔
1309
                          / CLOCKS_PER_SEC);
1310
#  endif
1311
        output->last_clock = last_clock;
76✔
1312
# elif defined(HAVE_CLOCK_WIN)
1313
        last_clock = clock_win();
1314
        dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
1315
                          / CLOCKS_PER_SEC);
1316
        output->last_clock = last_clock;
1317
# else
1318
        dulation = 0;
1319
# endif
1320
        if (dulation < 1000 * 10 * delay) {
76!
1321
# if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1322
            tv.tv_sec = 0;
76✔
1323
            tv.tv_nsec = (long)((1000 * 10 * delay - dulation) * 1000);
76✔
1324
#  if defined(HAVE_NANOSLEEP)
1325
            nanosleep(&tv, NULL);
76✔
1326
#  else
1327
            nanosleep_win(&tv, NULL);
1328
#  endif
1329
# endif
1330
        }
74✔
1331
    }
74✔
1332
#endif
1333

1334
    pixbuf = sixel_frame_get_pixels(frame);
411✔
1335
    memcpy(p, pixbuf, (size_t)(width * height * depth));
411✔
1336

1337
    status = sixel_output_convert_colorspace(output, p, size);
411✔
1338
    if (SIXEL_FAILED(status)) {
411!
1339
        goto end;
×
1340
    }
1341

1342
    if (encoder->cancel_flag && *encoder->cancel_flag) {
411!
1343
        goto end;
×
1344
    }
1345

1346
    status = sixel_encoder_convert_palette(encoder,
548✔
1347
                                           output,
137✔
1348
                                           dither,
137✔
1349
                                           frame_colorspace,
137✔
1350
                                           pixelformat,
137✔
1351
                                           &palette_ctx);
1352
    if (SIXEL_FAILED(status)) {
411!
1353
        goto end;
×
1354
    }
1355

1356
    status = sixel_encode(p, width, height, depth, dither, output);
411✔
1357
    if (status != SIXEL_OK) {
411!
1358
        goto end;
×
1359
    }
1360

1361
end:
274✔
1362
    sixel_encoder_restore_palette(encoder, dither, &palette_ctx);
411✔
1363
    output->pixelformat = pixelformat;
411✔
1364
    output->source_colorspace = frame_colorspace;
411✔
1365
    sixel_allocator_free(encoder->allocator, p);
411✔
1366

1367
    return status;
411✔
1368
}
1369

1370

1371
static SIXELSTATUS
1372
sixel_encoder_output_with_macro(
93✔
1373
    sixel_frame_t   /* in */ *frame,
1374
    sixel_dither_t  /* in */ *dither,
1375
    sixel_output_t  /* in */ *output,
1376
    sixel_encoder_t /* in */ *encoder)
1377
{
1378
    SIXELSTATUS status = SIXEL_OK;
93✔
1379
    enum { message_buffer_size = 256 };
1380
    char buffer[message_buffer_size];
1381
    int nwrite;
1382
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1383
    int dulation;
1384
    struct timespec tv;
1385
#endif
1386
    int width;
1387
    int height;
1388
    int pixelformat;
1389
    int depth;
1390
    size_t size = 0;
93✔
1391
    int frame_colorspace = SIXEL_COLORSPACE_GAMMA;
93✔
1392
    unsigned char *converted = NULL;
93✔
1393
    palette_conversion_t palette_ctx;
1394
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1395
    int delay;
1396
#endif
1397
#if defined(HAVE_CLOCK) || defined(HAVE_CLOCK_WIN)
1398
    sixel_clock_t last_clock;
1399
#endif
1400

1401
    memset(&palette_ctx, 0, sizeof(palette_ctx));
93✔
1402

1403
#if defined(HAVE_CLOCK)
1404
    if (output->last_clock == 0) {
93!
1405
        output->last_clock = clock();
93✔
1406
    }
37✔
1407
#elif defined(HAVE_CLOCK_WIN)
1408
    if (output->last_clock == 0) {
1409
        output->last_clock = clock_win();
1410
    }
1411
#endif
1412

1413
    width = sixel_frame_get_width(frame);
93✔
1414
    height = sixel_frame_get_height(frame);
93✔
1415
    pixelformat = sixel_frame_get_pixelformat(frame);
93✔
1416
    depth = sixel_helper_compute_depth(pixelformat);
93✔
1417
    if (depth < 0) {
93!
1418
        status = SIXEL_LOGIC_ERROR;
×
1419
        sixel_helper_set_additional_message(
×
1420
            "sixel_encoder_output_with_macro: "
1421
            "sixel_helper_compute_depth() failed.");
1422
        goto end;
×
1423
    }
1424

1425
    frame_colorspace = sixel_frame_get_colorspace(frame);
93✔
1426
    size = (size_t)width * (size_t)height * (size_t)depth;
93✔
1427
    converted = (unsigned char *)sixel_allocator_malloc(
93✔
1428
        encoder->allocator, size);
37✔
1429
    if (converted == NULL) {
93!
1430
        sixel_helper_set_additional_message(
×
1431
            "sixel_encoder_output_with_macro: "
1432
            "sixel_allocator_malloc() failed.");
1433
        status = SIXEL_BAD_ALLOCATION;
×
1434
        goto end;
×
1435
    }
1436

1437
    memcpy(converted, sixel_frame_get_pixels(frame), size);
93✔
1438
    output->pixelformat = pixelformat;
93✔
1439
    output->source_colorspace = frame_colorspace;
93✔
1440
    output->colorspace = encoder->output_colorspace;
93✔
1441
    status = sixel_output_convert_colorspace(output, converted, size);
93✔
1442
    if (SIXEL_FAILED(status)) {
93!
1443
        goto end;
×
1444
    }
1445

1446
    status = sixel_encoder_convert_palette(encoder,
130✔
1447
                                           output,
37✔
1448
                                           dither,
37✔
1449
                                           frame_colorspace,
37✔
1450
                                           pixelformat,
37✔
1451
                                           &palette_ctx);
1452
    if (SIXEL_FAILED(status)) {
93!
1453
        goto end;
×
1454
    }
1455

1456
    if (sixel_frame_get_loop_no(frame) == 0) {
93✔
1457
        if (encoder->macro_number >= 0) {
55!
1458
            nwrite = sprintf(buffer, "\033P%d;0;1!z",
1✔
1459
                             encoder->macro_number);
1460
        } else {
1✔
1461
            nwrite = sprintf(buffer, "\033P%d;0;1!z",
54✔
1462
                             sixel_frame_get_frame_no(frame));
1463
        }
1464
        if (nwrite < 0) {
55!
1465
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1466
            sixel_helper_set_additional_message(
×
1467
                "sixel_encoder_output_with_macro: sprintf() failed.");
1468
            goto end;
×
1469
        }
1470
        nwrite = sixel_write_callback(buffer,
74✔
1471
                                      (int)strlen(buffer),
55✔
1472
                                      &encoder->outfd);
55✔
1473
        if (nwrite < 0) {
55!
1474
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1475
            sixel_helper_set_additional_message(
×
1476
                "sixel_encoder_output_with_macro: "
1477
                "sixel_write_callback() failed.");
1478
            goto end;
×
1479
        }
1480

1481
        status = sixel_encode(converted,
74✔
1482
                              width,
19✔
1483
                              height,
19✔
1484
                              depth,
19✔
1485
                              dither,
19✔
1486
                              output);
19✔
1487
        if (SIXEL_FAILED(status)) {
55!
1488
            goto end;
×
1489
        }
1490

1491
        nwrite = sixel_write_callback("\033\\", 2, &encoder->outfd);
55✔
1492
        if (nwrite < 0) {
55!
1493
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1494
            sixel_helper_set_additional_message(
×
1495
                "sixel_encoder_output_with_macro: "
1496
                "sixel_write_callback() failed.");
1497
            goto end;
×
1498
        }
1499
    }
19✔
1500
    if (encoder->macro_number < 0) {
129✔
1501
        nwrite = sprintf(buffer, "\033[%d*z",
90✔
1502
                         sixel_frame_get_frame_no(frame));
1503
        if (nwrite < 0) {
90!
1504
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1505
            sixel_helper_set_additional_message(
×
1506
                "sixel_encoder_output_with_macro: sprintf() failed.");
1507
        }
1508
        nwrite = sixel_write_callback(buffer,
126✔
1509
                                      (int)strlen(buffer),
90✔
1510
                                      &encoder->outfd);
90✔
1511
        if (nwrite < 0) {
90!
1512
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1513
            sixel_helper_set_additional_message(
×
1514
                "sixel_encoder_output_with_macro: "
1515
                "sixel_write_callback() failed.");
1516
            goto end;
×
1517
        }
1518
#if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1519
        delay = sixel_frame_get_delay(frame);
90✔
1520
        if (delay > 0 && !encoder->fignore_delay) {
90!
1521
# if defined(HAVE_CLOCK)
1522
            last_clock = clock();
63✔
1523
/* https://stackoverflow.com/questions/16697005/clock-and-clocks-per-sec-on-osx-10-7 */
1524
#  if defined(__APPLE__)
1525
            dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
54✔
1526
                             / 100000);
27✔
1527
#  else
1528
            dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
36✔
1529
                             / CLOCKS_PER_SEC);
1530
#  endif
1531
            output->last_clock = last_clock;
63✔
1532
# elif defined(HAVE_CLOCK_WIN)
1533
            last_clock = clock_win();
1534
            dulation = (int)((last_clock - output->last_clock) * 1000 * 1000
1535
                             / CLOCKS_PER_SEC);
1536
            output->last_clock = last_clock;
1537
# else
1538
            dulation = 0;
1539
# endif
1540
            if (dulation < 1000 * 10 * delay) {
63!
1541
# if defined(HAVE_NANOSLEEP) || defined(HAVE_NANOSLEEP_WIN)
1542
                tv.tv_sec = 0;
59✔
1543
                tv.tv_nsec = (long)((1000 * 10 * delay - dulation) * 1000);
59✔
1544
#  if defined(HAVE_NANOSLEEP)
1545
                nanosleep(&tv, NULL);
59✔
1546
#  else
1547
                nanosleep_win(&tv, NULL);
1548
#  endif
1549
# endif
1550
            }
23✔
1551
        }
27✔
1552
#endif
1553
    }
36✔
1554

1555
end:
20✔
1556
    sixel_encoder_restore_palette(encoder, dither, &palette_ctx);
93✔
1557
    output->pixelformat = pixelformat;
93✔
1558
    output->source_colorspace = frame_colorspace;
93✔
1559
    sixel_allocator_free(encoder->allocator, converted);
93✔
1560

1561
    return status;
93✔
1562
}
1563

1564

1565
static SIXELSTATUS
1566
sixel_encoder_emit_iso2022_chars(
×
1567
    sixel_encoder_t *encoder,
1568
    sixel_frame_t *frame
1569
)
1570
{
1571
    char *buf_p, *buf;
1572
    int col, row;
1573
    int charset = encoder->start_dscs;
×
1574
    int is_96cs = 0;
×
1575
    unsigned int code;
1576
    int num_cols, num_rows;
1577
    SIXELSTATUS status;
1578
    size_t alloc_size;
1579
    int nwrite;
1580
    int target_fd;
1581

1582
    code = 0x100020 + (is_96cs ? 0x80 : 0) + charset * 0x100;
×
1583
    num_cols = (sixel_frame_get_width(frame) + encoder->cell_width - 1)
×
1584
             / encoder->cell_width;
×
1585
    num_rows = (sixel_frame_get_height(frame) + encoder->cell_height - 1)
×
1586
             / encoder->cell_height;
×
1587

1588
    /* cols x rows + designation(4 chars) + SI + SO + LFs */
1589
    alloc_size = num_cols * num_rows + (num_cols * num_rows / 96 + 1) * 4 + 2 + num_rows;
×
1590
    buf_p = buf = sixel_allocator_malloc(encoder->allocator, alloc_size);
×
1591
    if (buf == NULL) {
×
1592
        sixel_helper_set_additional_message(
×
1593
            "sixel_encoder_emit_iso2022_chars: sixel_allocator_malloc() failed.");
1594
        status = SIXEL_BAD_ALLOCATION;
×
1595
        goto end;
×
1596
    }
1597

1598
    code = 0x20;
×
1599
    *(buf_p++) = '\016';  /* SI */
×
1600
    *(buf_p++) = '\033';
×
1601
    *(buf_p++) = ')';
×
1602
    *(buf_p++) = ' ';
×
1603
    *(buf_p++) = charset;
×
1604
    for(row = 0; row < num_rows; row++) {
×
1605
        for(col = 0; col < num_cols; col++) {
×
1606
            if ((code & 0x7f) == 0x0) {
×
1607
                if (charset == 0x7e) {
×
1608
                    is_96cs = 1 - is_96cs;
×
1609
                    charset = '0';
×
1610
                } else {
1611
                    charset++;
×
1612
                }
1613
                code = 0x20;
×
1614
                *(buf_p++) = '\033';
×
1615
                *(buf_p++) = is_96cs ? '-': ')';
×
1616
                *(buf_p++) = ' ';
×
1617
                *(buf_p++) = charset;
×
1618
            }
1619
            *(buf_p++) = code++;
×
1620
        }
1621
        *(buf_p++) = '\n';
×
1622
    }
1623
    *(buf_p++) = '\017';  /* SO */
×
1624

1625
    if (encoder->tile_outfd >= 0) {
×
1626
        target_fd = encoder->tile_outfd;
×
1627
    } else {
1628
        target_fd = encoder->outfd;
×
1629
    }
1630

1631
    nwrite = write(target_fd, buf, buf_p - buf);
×
1632
    if (nwrite != buf_p - buf) {
×
1633
        sixel_helper_set_additional_message(
×
1634
            "sixel_encoder_emit_iso2022_chars: write() failed.");
1635
        status = SIXEL_RUNTIME_ERROR;
×
1636
        goto end;
×
1637
    }
1638

1639
    sixel_allocator_free(encoder->allocator, buf);
×
1640

1641
    status = SIXEL_OK;
×
1642

1643
end:
1644
    return status;
×
1645
}
1646

1647

1648
/*
1649
 * This routine is derived from mlterm's drcssixel.c
1650
 * (https://raw.githubusercontent.com/arakiken/mlterm/master/drcssixel/drcssixel.c).
1651
 * The original implementation is credited to Araki Ken.
1652
 * Adjusted here to integrate with libsixel's encoder pipeline.
1653
 */
1654
static SIXELSTATUS
1655
sixel_encoder_emit_drcsmmv2_chars(
×
1656
    sixel_encoder_t *encoder,
1657
    sixel_frame_t *frame
1658
)
1659
{
1660
    char *buf_p, *buf;
1661
    int col, row;
1662
    int charset = encoder->start_dscs;
×
1663
    int is_96cs = 0;
×
1664
    unsigned int code;
1665
    int num_cols, num_rows;
1666
    SIXELSTATUS status;
1667
    size_t alloc_size;
1668
    int nwrite;
1669
    int target_fd;
1670

1671
    code = 0x100020 + (is_96cs ? 0x80 : 0) + charset * 0x100;
×
1672
    num_cols = (sixel_frame_get_width(frame) + encoder->cell_width - 1)
×
1673
             / encoder->cell_width;
×
1674
    num_rows = (sixel_frame_get_height(frame) + encoder->cell_height - 1)
×
1675
             / encoder->cell_height;
×
1676

1677
    /* cols x rows x 4(out of BMP) + rows(LFs) */
1678
    alloc_size = num_cols * num_rows * 4 + num_rows;
×
1679
    buf_p = buf = sixel_allocator_malloc(encoder->allocator, alloc_size);
×
1680
    if (buf == NULL) {
×
1681
        sixel_helper_set_additional_message(
×
1682
            "sixel_encoder_emit_drcsmmv2_chars: sixel_allocator_malloc() failed.");
1683
        status = SIXEL_BAD_ALLOCATION;
×
1684
        goto end;
×
1685
    }
1686

1687
    for(row = 0; row < num_rows; row++) {
×
1688
        for(col = 0; col < num_cols; col++) {
×
1689
            *(buf_p++) = ((code >> 18) & 0x07) | 0xf0;
×
1690
            *(buf_p++) = ((code >> 12) & 0x3f) | 0x80;
×
1691
            *(buf_p++) = ((code >> 6) & 0x3f) | 0x80;
×
1692
            *(buf_p++) = (code & 0x3f) | 0x80;
×
1693
            code++;
×
1694
            if ((code & 0x7f) == 0x0) {
×
1695
                if (charset == 0x7e) {
×
1696
                    is_96cs = 1 - is_96cs;
×
1697
                    charset = '0';
×
1698
                } else {
1699
                    charset++;
×
1700
                }
1701
                code = 0x100020 + (is_96cs ? 0x80 : 0) + charset * 0x100;
×
1702
            }
1703
        }
1704
        *(buf_p++) = '\n';
×
1705
    }
1706

1707
    if (charset == 0x7e) {
×
1708
        is_96cs = 1 - is_96cs;
×
1709
    } else {
1710
        charset = '0';
×
1711
        charset++;
×
1712
    }
1713
    if (encoder->tile_outfd >= 0) {
×
1714
        target_fd = encoder->tile_outfd;
×
1715
    } else {
1716
        target_fd = encoder->outfd;
×
1717
    }
1718

1719
    nwrite = write(target_fd, buf, buf_p - buf);
×
1720
    if (nwrite != buf_p - buf) {
×
1721
        sixel_helper_set_additional_message(
×
1722
            "sixel_encoder_emit_drcsmmv2_chars: write() failed.");
1723
        status = SIXEL_RUNTIME_ERROR;
×
1724
        goto end;
×
1725
    }
1726

1727
    sixel_allocator_free(encoder->allocator, buf);
×
1728

1729
    status = SIXEL_OK;
×
1730

1731
end:
1732
    return status;
×
1733
}
1734

1735
static SIXELSTATUS
1736
sixel_encoder_encode_frame(
518✔
1737
    sixel_encoder_t *encoder,
1738
    sixel_frame_t   *frame,
1739
    sixel_output_t  *output)
1740
{
1741
    SIXELSTATUS status = SIXEL_FALSE;
518✔
1742
    sixel_dither_t *dither = NULL;
518✔
1743
    int height;
1744
    int is_animation = 0;
518✔
1745
    int nwrite;
1746
    char buf[256];
1747
    sixel_write_function fn_write = NULL;
518✔
1748

1749
    /* evaluate -w, -h, and -c option: crop/scale input source */
1750
    if (encoder->clipfirst) {
518✔
1751
        /* clipping */
1752
        status = sixel_encoder_do_clip(encoder, frame);
8✔
1753
        if (SIXEL_FAILED(status)) {
8!
1754
            goto end;
2✔
1755
        }
1756

1757
        /* scaling */
1758
        status = sixel_encoder_do_resize(encoder, frame);
6✔
1759
        if (SIXEL_FAILED(status)) {
6!
1760
            goto end;
×
1761
        }
1762
    } else {
2✔
1763
        /* scaling */
1764
        status = sixel_encoder_do_resize(encoder, frame);
510✔
1765
        if (SIXEL_FAILED(status)) {
510✔
1766
            goto end;
6✔
1767
        }
1768

1769
        /* clipping */
1770
        status = sixel_encoder_do_clip(encoder, frame);
504✔
1771
        if (SIXEL_FAILED(status)) {
504!
1772
            goto end;
1✔
1773
        }
1774
    }
1775

1776
    status = sixel_frame_ensure_colorspace(frame,
686✔
1777
                                           encoder->working_colorspace);
177✔
1778
    if (SIXEL_FAILED(status)) {
509!
1779
        goto end;
×
1780
    }
1781

1782
    /* prepare dither context */
1783
    status = sixel_encoder_prepare_palette(encoder, frame, &dither);
509✔
1784
    if (status != SIXEL_OK) {
509✔
1785
        dither = NULL;
5✔
1786
        goto end;
5✔
1787
    }
1788

1789
    if (encoder->dither_cache != NULL) {
504!
1790
        encoder->dither_cache = dither;
×
1791
        sixel_dither_ref(dither);
×
1792
    }
1793

1794
    if (encoder->fdrcs) {
504!
1795
        status = sixel_encoder_ensure_cell_size(encoder);
×
1796
        if (SIXEL_FAILED(status)) {
×
1797
            goto end;
×
1798
        }
1799
        if (encoder->fuse_macro || encoder->macro_number >= 0) {
×
1800
            sixel_helper_set_additional_message(
×
1801
                "drcs option cannot be used together with macro output.");
1802
            status = SIXEL_BAD_ARGUMENT;
×
1803
            goto end;
×
1804
        }
1805
    }
1806

1807
    /* evaluate -v option: print palette */
1808
    if (encoder->verbose) {
504✔
1809
        if ((sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_PALETTE)) {
9✔
1810
            sixel_debug_print_palette(dither);
3✔
1811
        }
1✔
1812
    }
3✔
1813

1814
    /* evaluate -d option: set method for diffusion */
1815
    sixel_dither_set_diffusion_type(dither, encoder->method_for_diffuse);
504✔
1816
    sixel_dither_set_diffusion_scan(dither, encoder->method_for_scan);
504✔
1817
    sixel_dither_set_diffusion_carry(dither, encoder->method_for_carry);
504✔
1818

1819
    /* evaluate -C option: set complexion score */
1820
    if (encoder->complexion > 1) {
504✔
1821
        sixel_dither_set_complexion_score(dither, encoder->complexion);
6✔
1822
    }
2✔
1823

1824
    if (output) {
504!
1825
        sixel_output_ref(output);
×
1826
    } else {
1827
        /* create output context */
1828
        if (encoder->fuse_macro || encoder->macro_number >= 0) {
504✔
1829
            /* -u or -n option */
1830
            fn_write = sixel_hex_write_callback;
93✔
1831
        } else {
37✔
1832
            fn_write = sixel_write_callback;
411✔
1833
        }
1834
        status = sixel_output_new(&output,
504✔
1835
                                  fn_write,
174✔
1836
                                  &encoder->outfd,
504✔
1837
                                  encoder->allocator);
174✔
1838
        if (SIXEL_FAILED(status)) {
504!
1839
            goto end;
×
1840
        }
1841
    }
1842

1843
    if (encoder->fdrcs) {
504!
1844
        sixel_output_set_skip_dcs_envelope(output, 1);
×
1845
        sixel_output_set_skip_header(output, 1);
×
1846
    }
1847

1848
    sixel_output_set_8bit_availability(output, encoder->f8bit);
504✔
1849
    sixel_output_set_gri_arg_limit(output, encoder->has_gri_arg_limit);
504✔
1850
    sixel_output_set_palette_type(output, encoder->palette_type);
504✔
1851
    sixel_output_set_penetrate_multiplexer(
504✔
1852
        output, encoder->penetrate_multiplexer);
174✔
1853
    sixel_output_set_encode_policy(output, encoder->encode_policy);
504✔
1854
    sixel_output_set_ormode(output, encoder->ormode);
504✔
1855

1856
    if (sixel_frame_get_multiframe(frame) && !encoder->fstatic) {
504!
1857
        if (sixel_frame_get_loop_no(frame) != 0 || sixel_frame_get_frame_no(frame) != 0) {
117✔
1858
            is_animation = 1;
108✔
1859
        }
42✔
1860
        height = sixel_frame_get_height(frame);
117✔
1861
        (void) sixel_tty_scroll(sixel_write_callback, encoder->outfd, height, is_animation);
117✔
1862
    }
45✔
1863

1864
    if (encoder->cancel_flag && *encoder->cancel_flag) {
504!
1865
        status = SIXEL_INTERRUPTED;
×
1866
        goto end;
×
1867
    }
1868

1869
    if (encoder->fdrcs) {  /* -@ option */
504!
1870
        nwrite = sprintf(buf,
×
1871
                         "%s%s1;0;0;%d;1;3;%d;0{ %c",
1872
                         (encoder->drcs_mmv > 0) ? (
×
1873
                             encoder->f8bit ? "\233?8800h": "\033[?8800h"
×
1874
                         ): "",
1875
                         encoder->f8bit ? "\220": "\033P",
1876
                         encoder->cell_width,
1877
                         encoder->cell_height,
1878
                         encoder->start_dscs);
×
1879
        if (nwrite < 0) {
×
1880
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1881
            sixel_helper_set_additional_message(
×
1882
                "sixel_encoder_encode_frame: sprintf() failed.");
1883
            goto end;
×
1884
        }
1885
        nwrite = fn_write(buf, nwrite, &encoder->outfd);
×
1886
        if (nwrite < 0) {
×
1887
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1888
            sixel_helper_set_additional_message(
×
1889
                "sixel_encoder_encode_frame: write() failed.");
1890
            goto end;
×
1891
        }
1892
    }
1893

1894
    /* output sixel: junction of multi-frame processing strategy */
1895
    if (encoder->fuse_macro) {  /* -u option */
504✔
1896
        /* use macro */
1897
        status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
90✔
1898
    } else if (encoder->macro_number >= 0) { /* -n option */
450✔
1899
        /* use macro */
1900
        status = sixel_encoder_output_with_macro(frame, dither, output, encoder);
3✔
1901
    } else {
1✔
1902
        /* do not use macro */
1903
        status = sixel_encoder_output_without_macro(frame, dither, output, encoder);
411✔
1904
    }
1905
    if (SIXEL_FAILED(status)) {
504!
1906
        goto end;
×
1907
    }
1908

1909
    if (encoder->cancel_flag && *encoder->cancel_flag) {
504!
1910
        nwrite = sixel_write_callback("\x18\033\\", 3, &encoder->outfd);
×
1911
        if (nwrite < 0) {
×
1912
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1913
            sixel_helper_set_additional_message(
×
1914
                "sixel_encoder_encode_frame: sixel_write_callback() failed.");
1915
            goto end;
×
1916
        }
1917
        status = SIXEL_INTERRUPTED;
×
1918
    }
1919
    if (SIXEL_FAILED(status)) {
504!
1920
        goto end;
×
1921
    }
1922

1923
    if (encoder->fdrcs) {  /* -@ option */
504!
1924
        if (encoder->f8bit) {
×
1925
            nwrite = fn_write("\234", 1, &encoder->outfd);
×
1926
        } else {
1927
            nwrite = fn_write("\033\\", 2, &encoder->outfd);
×
1928
        }
1929
        if (nwrite < 0) {
×
1930
            status = (SIXEL_LIBC_ERROR | (errno & 0xff));
×
1931
            sixel_helper_set_additional_message(
×
1932
                "sixel_encoder_encode_frame: fn_write() failed.");
1933
            goto end;
×
1934
        }
1935

1936
        if (encoder->tile_outfd >= 0) {
×
1937
            if (encoder->drcs_mmv == 0) {
×
1938
                status = sixel_encoder_emit_iso2022_chars(encoder, frame);
×
1939
                if (SIXEL_FAILED(status)) {
×
1940
                    goto end;
×
1941
                }
1942
            } else {
1943
                status = sixel_encoder_emit_drcsmmv2_chars(encoder, frame);
×
1944
                if (SIXEL_FAILED(status)) {
×
1945
                    goto end;
×
1946
                }
1947
            }
1948
        }
1949
    }
1950

1951

1952
end:
330✔
1953
    if (output) {
518✔
1954
        sixel_output_unref(output);
504✔
1955
    }
174✔
1956
    if (dither) {
518✔
1957
        sixel_dither_unref(dither);
504✔
1958
    }
174✔
1959

1960
    return status;
518✔
1961
}
1962

1963

1964
/* create encoder object */
1965
SIXELAPI SIXELSTATUS
1966
sixel_encoder_new(
498✔
1967
    sixel_encoder_t     /* out */ **ppencoder, /* encoder object to be created */
1968
    sixel_allocator_t   /* in */  *allocator)  /* allocator, null if you use
1969
                                                  default allocator */
1970
{
1971
    SIXELSTATUS status = SIXEL_FALSE;
498✔
1972
    char const *env_default_bgcolor = NULL;
498✔
1973
    char const *env_default_ncolors = NULL;
498✔
1974
    int ncolors;
1975
#if HAVE__DUPENV_S
1976
    errno_t e;
1977
    size_t len;
1978
#endif  /* HAVE__DUPENV_S */
1979

1980
    if (allocator == NULL) {
498!
1981
        status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
498✔
1982
        if (SIXEL_FAILED(status)) {
498!
1983
            goto end;
×
1984
        }
1985
    } else {
166✔
1986
        sixel_allocator_ref(allocator);
×
1987
    }
1988

1989
    *ppencoder
166✔
1990
        = (sixel_encoder_t *)sixel_allocator_malloc(allocator,
664✔
1991
                                                    sizeof(sixel_encoder_t));
1992
    if (*ppencoder == NULL) {
498!
1993
        sixel_helper_set_additional_message(
×
1994
            "sixel_encoder_new: sixel_allocator_malloc() failed.");
1995
        status = SIXEL_BAD_ALLOCATION;
×
1996
        sixel_allocator_unref(allocator);
×
1997
        goto end;
×
1998
    }
1999

2000
    (*ppencoder)->ref                   = 1;
498✔
2001
    (*ppencoder)->reqcolors             = (-1);
498✔
2002
    (*ppencoder)->force_palette         = 0;
498✔
2003
    (*ppencoder)->mapfile               = NULL;
498✔
2004
    (*ppencoder)->loader_order          = NULL;
498✔
2005
    (*ppencoder)->color_option          = SIXEL_COLOR_OPTION_DEFAULT;
498✔
2006
    (*ppencoder)->builtin_palette       = 0;
498✔
2007
    (*ppencoder)->method_for_diffuse    = SIXEL_DIFFUSE_AUTO;
498✔
2008
    (*ppencoder)->method_for_scan       = SIXEL_SCAN_AUTO;
498✔
2009
    (*ppencoder)->method_for_carry      = SIXEL_CARRY_AUTO;
498✔
2010
    (*ppencoder)->method_for_largest    = SIXEL_LARGE_AUTO;
498✔
2011
    (*ppencoder)->method_for_rep        = SIXEL_REP_AUTO;
498✔
2012
    (*ppencoder)->quality_mode          = SIXEL_QUALITY_AUTO;
498✔
2013
    (*ppencoder)->lut_policy            = SIXEL_LUT_POLICY_AUTO;
498✔
2014
    (*ppencoder)->method_for_resampling = SIXEL_RES_BILINEAR;
498✔
2015
    (*ppencoder)->loop_mode             = SIXEL_LOOP_AUTO;
498✔
2016
    (*ppencoder)->palette_type          = SIXEL_PALETTETYPE_AUTO;
498✔
2017
    (*ppencoder)->f8bit                 = 0;
498✔
2018
    (*ppencoder)->has_gri_arg_limit     = 0;
498✔
2019
    (*ppencoder)->finvert               = 0;
498✔
2020
    (*ppencoder)->fuse_macro            = 0;
498✔
2021
    (*ppencoder)->fdrcs                 = 0;
498✔
2022
    (*ppencoder)->fignore_delay         = 0;
498✔
2023
    (*ppencoder)->complexion            = 1;
498✔
2024
    (*ppencoder)->fstatic               = 0;
498✔
2025
    (*ppencoder)->cell_width            = 0;
498✔
2026
    (*ppencoder)->cell_height           = 0;
498✔
2027
    (*ppencoder)->pixelwidth            = (-1);
498✔
2028
    (*ppencoder)->pixelheight           = (-1);
498✔
2029
    (*ppencoder)->percentwidth          = (-1);
498✔
2030
    (*ppencoder)->percentheight         = (-1);
498✔
2031
    (*ppencoder)->clipx                 = 0;
498✔
2032
    (*ppencoder)->clipy                 = 0;
498✔
2033
    (*ppencoder)->clipwidth             = 0;
498✔
2034
    (*ppencoder)->clipheight            = 0;
498✔
2035
    (*ppencoder)->clipfirst             = 0;
498✔
2036
    (*ppencoder)->macro_number          = (-1);
498✔
2037
    (*ppencoder)->verbose               = 0;
498✔
2038
    (*ppencoder)->penetrate_multiplexer = 0;
498✔
2039
    (*ppencoder)->encode_policy         = SIXEL_ENCODEPOLICY_AUTO;
498✔
2040
    (*ppencoder)->working_colorspace    = SIXEL_COLORSPACE_GAMMA;
498✔
2041
    (*ppencoder)->output_colorspace     = SIXEL_COLORSPACE_GAMMA;
498✔
2042
    (*ppencoder)->ormode                = 0;
498✔
2043
    (*ppencoder)->pipe_mode             = 0;
498✔
2044
    (*ppencoder)->bgcolor               = NULL;
498✔
2045
    (*ppencoder)->outfd                 = STDOUT_FILENO;
498✔
2046
    (*ppencoder)->tile_outfd            = (-1);
498✔
2047
    (*ppencoder)->finsecure             = 0;
498✔
2048
    (*ppencoder)->cancel_flag           = NULL;
498✔
2049
    (*ppencoder)->dither_cache          = NULL;
498✔
2050
    (*ppencoder)->start_dscs            = '0';
498✔
2051
    (*ppencoder)->drcs_mmv              = 2;
498✔
2052
    (*ppencoder)->allocator             = allocator;
498✔
2053

2054
    /* evaluate environment variable ${SIXEL_BGCOLOR} */
2055
#if HAVE__DUPENV_S
2056
    e = _dupenv_s(&env_default_bgcolor, &len, "SIXEL_BGCOLOR");
2057
    if (e != (0)) {
2058
        sixel_helper_set_additional_message(
2059
            "failed to get environment variable $SIXEL_BGCOLOR.");
2060
        return (SIXEL_LIBC_ERROR | (e & 0xff));
2061
    }
2062
#else
2063
    env_default_bgcolor = getenv("SIXEL_BGCOLOR");
498✔
2064
#endif  /* HAVE__DUPENV_S */
2065
    if (env_default_bgcolor != NULL) {
498!
2066
        status = sixel_parse_x_colorspec(&(*ppencoder)->bgcolor,
×
2067
                                         env_default_bgcolor,
2068
                                         allocator);
2069
        if (SIXEL_FAILED(status)) {
×
2070
            goto error;
×
2071
        }
2072
    }
2073

2074
    /* evaluate environment variable ${SIXEL_COLORS} */
2075
#if HAVE__DUPENV_S
2076
    e = _dupenv_s(&env_default_bgcolor, &len, "SIXEL_COLORS");
2077
    if (e != (0)) {
2078
        sixel_helper_set_additional_message(
2079
            "failed to get environment variable $SIXEL_COLORS.");
2080
        return (SIXEL_LIBC_ERROR | (e & 0xff));
2081
    }
2082
#else
2083
    env_default_ncolors = getenv("SIXEL_COLORS");
498✔
2084
#endif  /* HAVE__DUPENV_S */
2085
    if (env_default_ncolors) {
498!
2086
        ncolors = atoi(env_default_ncolors); /* may overflow */
×
2087
        if (ncolors > 1 && ncolors <= SIXEL_PALETTE_MAX) {
×
2088
            (*ppencoder)->reqcolors = ncolors;
×
2089
        }
2090
    }
2091

2092
    /* success */
2093
    status = SIXEL_OK;
498✔
2094

2095
    goto end;
498✔
2096

2097
error:
2098
    sixel_allocator_free(allocator, *ppencoder);
×
2099
    sixel_allocator_unref(allocator);
×
2100
    *ppencoder = NULL;
×
2101

2102
end:
332✔
2103
#if HAVE__DUPENV_S
2104
    free(env_default_bgcolor);
2105
    free(env_default_ncolors);
2106
#endif  /* HAVE__DUPENV_S */
2107
    return status;
498✔
2108
}
2109

2110

2111
/* create encoder object (deprecated version) */
2112
SIXELAPI /* deprecated */ sixel_encoder_t *
2113
sixel_encoder_create(void)
×
2114
{
2115
    SIXELSTATUS status = SIXEL_FALSE;
×
2116
    sixel_encoder_t *encoder = NULL;
×
2117

2118
    status = sixel_encoder_new(&encoder, NULL);
×
2119
    if (SIXEL_FAILED(status)) {
×
2120
        return NULL;
×
2121
    }
2122

2123
    return encoder;
×
2124
}
2125

2126

2127
/* destroy encoder object */
2128
static void
2129
sixel_encoder_destroy(sixel_encoder_t *encoder)
498✔
2130
{
2131
    sixel_allocator_t *allocator;
2132

2133
    if (encoder) {
498!
2134
        allocator = encoder->allocator;
498✔
2135
        sixel_allocator_free(allocator, encoder->mapfile);
498✔
2136
        sixel_allocator_free(allocator, encoder->loader_order);
498✔
2137
        sixel_allocator_free(allocator, encoder->bgcolor);
498✔
2138
        sixel_dither_unref(encoder->dither_cache);
498✔
2139
        if (encoder->outfd
504!
2140
            && encoder->outfd != STDOUT_FILENO
498!
2141
            && encoder->outfd != STDERR_FILENO) {
178!
2142
#if HAVE__CLOSE
2143
            (void) _close(encoder->outfd);
2144
#else
2145
            (void) close(encoder->outfd);
18✔
2146
#endif  /* HAVE__CLOSE */
2147
        }
6✔
2148
        if (encoder->tile_outfd >= 0
498!
2149
            && encoder->tile_outfd != encoder->outfd
166!
2150
            && encoder->tile_outfd != STDOUT_FILENO
×
2151
            && encoder->tile_outfd != STDERR_FILENO) {
×
2152
#if HAVE__CLOSE
2153
            (void) _close(encoder->tile_outfd);
2154
#else
2155
            (void) close(encoder->tile_outfd);
×
2156
#endif  /* HAVE__CLOSE */
2157
        }
2158
        sixel_allocator_free(allocator, encoder);
498✔
2159
        sixel_allocator_unref(allocator);
498✔
2160
    }
166✔
2161
}
498✔
2162

2163

2164
/* increase reference count of encoder object (thread-unsafe) */
2165
SIXELAPI void
2166
sixel_encoder_ref(sixel_encoder_t *encoder)
1,089✔
2167
{
2168
    /* TODO: be thread safe */
2169
    ++encoder->ref;
1,089✔
2170
}
1,089✔
2171

2172

2173
/* decrease reference count of encoder object (thread-unsafe) */
2174
SIXELAPI void
2175
sixel_encoder_unref(sixel_encoder_t *encoder)
1,587✔
2176
{
2177
    /* TODO: be thread safe */
2178
    if (encoder != NULL && --encoder->ref == 0) {
1,587!
2179
        sixel_encoder_destroy(encoder);
498✔
2180
    }
166✔
2181
}
1,587✔
2182

2183

2184
/* set cancel state flag to encoder object */
2185
SIXELAPI SIXELSTATUS
2186
sixel_encoder_set_cancel_flag(
417✔
2187
    sixel_encoder_t /* in */ *encoder,
2188
    int             /* in */ *cancel_flag
2189
)
2190
{
2191
    SIXELSTATUS status = SIXEL_OK;
417✔
2192

2193
    encoder->cancel_flag = cancel_flag;
417✔
2194

2195
    return status;
417✔
2196
}
2197

2198

2199
/* set an option flag to encoder object */
2200
SIXELAPI SIXELSTATUS
2201
sixel_encoder_setopt(
672✔
2202
    sixel_encoder_t /* in */ *encoder,
2203
    int             /* in */ arg,
2204
    char const      /* in */ *value)
2205
{
2206
    SIXELSTATUS status = SIXEL_FALSE;
672✔
2207
    int number;
2208
    int parsed;
2209
    char unit[32];
2210
    char lowered[16];
2211
    size_t len;
2212
    size_t i;
2213
    long parsed_reqcolors;
2214
    char *endptr;
2215
    int forced_palette;
2216

2217
    sixel_encoder_ref(encoder);
672✔
2218

2219
    switch(arg) {
672!
2220
    case SIXEL_OPTFLAG_OUTFILE:  /* o */
12✔
2221
        if (*value == '\0') {
18!
2222
            sixel_helper_set_additional_message(
×
2223
                "no file name specified.");
2224
            status = SIXEL_BAD_ARGUMENT;
×
2225
            goto end;
×
2226
        }
2227
        if (strcmp(value, "-") != 0) {
18!
2228
            if (encoder->outfd && encoder->outfd != STDOUT_FILENO) {
18!
2229
#if HAVE__CLOSE
2230
                (void) _close(encoder->outfd);
2231
#else
2232
                (void) close(encoder->outfd);
×
2233
#endif  /* HAVE__CLOSE */
2234
            }
2235
#if HAVE__OPEN
2236
            encoder->outfd = _open(value,
2237
                                   O_RDWR|O_CREAT|O_TRUNC,
2238
                                   S_IRUSR|S_IWUSR);
2239
#else
2240
            encoder->outfd = open(value,
18✔
2241
                                  O_RDWR|O_CREAT|O_TRUNC,
2242
                                  S_IRUSR|S_IWUSR);
2243
#endif  /* HAVE__OPEN */
2244
        }
6✔
2245
        break;
18✔
2246
    case SIXEL_OPTFLAG_ALT_CHARSET_PATH:  /* T */
2247
        if (*value == '\0') {
×
2248
            sixel_helper_set_additional_message(
×
2249
                "no file name specified.");
2250
            status = SIXEL_BAD_ARGUMENT;
×
2251
            goto end;
×
2252
        }
2253
        if (encoder->tile_outfd >= 0
×
2254
            && encoder->tile_outfd != encoder->outfd
×
2255
            && encoder->tile_outfd != STDOUT_FILENO
×
2256
            && encoder->tile_outfd != STDERR_FILENO) {
×
2257
#if HAVE__CLOSE
2258
            (void) _close(encoder->tile_outfd);
2259
#else
2260
            (void) close(encoder->tile_outfd);
×
2261
#endif  /* HAVE__CLOSE */
2262
        }
2263
        encoder->tile_outfd = (-1);
×
2264
        if (strcmp(value, "-") == 0) {
×
2265
            encoder->tile_outfd = STDOUT_FILENO;
×
2266
        } else {
2267
#if HAVE__OPEN
2268
            encoder->tile_outfd = _open(value,
2269
                                           O_RDWR|O_CREAT|O_TRUNC,
2270
                                           S_IRUSR|S_IWUSR);
2271
#else
2272
            encoder->tile_outfd = open(value,
×
2273
                                          O_RDWR|O_CREAT|O_TRUNC,
2274
                                          S_IRUSR|S_IWUSR);
2275
#endif  /* HAVE__OPEN */
2276
            if (encoder->tile_outfd < 0) {
×
2277
                sixel_helper_set_additional_message(
×
2278
                    "sixel_encoder_setopt: failed to open tile"
2279
                    " output path.");
2280
                status = SIXEL_RUNTIME_ERROR;
×
2281
                goto end;
×
2282
            }
2283
        }
2284
        break;
×
2285
    case SIXEL_OPTFLAG_7BIT_MODE:  /* 7 */
10✔
2286
        encoder->f8bit = 0;
15✔
2287
        break;
15✔
2288
    case SIXEL_OPTFLAG_8BIT_MODE:  /* 8 */
12✔
2289
        encoder->f8bit = 1;
18✔
2290
        break;
18✔
2291
    case SIXEL_OPTFLAG_HAS_GRI_ARG_LIMIT:  /* R */
2292
        encoder->has_gri_arg_limit = 1;
×
2293
        break;
×
2294
    case SIXEL_OPTFLAG_COLORS:  /* p */
18✔
2295
        forced_palette = 0;
27✔
2296
        errno = 0;
27✔
2297
        endptr = NULL;
27✔
2298
        if (*value == '!' && value[1] == '\0') {
27!
2299
            /*
2300
             * Force the default palette size even when the median cut
2301
             * finished early.
2302
             *
2303
             *   requested colors
2304
             *          |
2305
             *          v
2306
             *        [ 256 ]  <--- "-p!" triggers this shortcut
2307
             */
2308
            parsed_reqcolors = SIXEL_PALETTE_MAX;
×
2309
            forced_palette = 1;
×
2310
        } else {
2311
            parsed_reqcolors = strtol(value, &endptr, 10);
27✔
2312
            if (endptr != NULL && *endptr == '!') {
27!
2313
                forced_palette = 1;
×
2314
                ++endptr;
×
2315
            }
2316
            if (errno == ERANGE || endptr == value) {
27!
2317
                sixel_helper_set_additional_message(
×
2318
                    "cannot parse -p/--colors option.");
2319
                status = SIXEL_BAD_ARGUMENT;
×
2320
                goto end;
×
2321
            }
2322
            if (endptr != NULL && *endptr != '\0') {
27!
2323
                sixel_helper_set_additional_message(
×
2324
                    "cannot parse -p/--colors option.");
2325
                status = SIXEL_BAD_ARGUMENT;
×
2326
                goto end;
×
2327
            }
2328
        }
2329
        if (parsed_reqcolors < 1) {
27!
2330
            sixel_helper_set_additional_message(
×
2331
                "-p/--colors parameter must be 1 or more.");
2332
            status = SIXEL_BAD_ARGUMENT;
×
2333
            goto end;
×
2334
        }
2335
        if (parsed_reqcolors > SIXEL_PALETTE_MAX) {
27!
2336
            sixel_helper_set_additional_message(
×
2337
                "-p/--colors parameter must be less then or equal to 256.");
2338
            status = SIXEL_BAD_ARGUMENT;
×
2339
            goto end;
×
2340
        }
2341
        encoder->reqcolors = (int)parsed_reqcolors;
27✔
2342
        encoder->force_palette = forced_palette;
27✔
2343
        break;
27✔
2344
    case SIXEL_OPTFLAG_MAPFILE:  /* m */
18✔
2345
        if (encoder->mapfile) {
27✔
2346
            sixel_allocator_free(encoder->allocator, encoder->mapfile);
3✔
2347
        }
1✔
2348
        encoder->mapfile = arg_strdup(value, encoder->allocator);
27✔
2349
        if (encoder->mapfile == NULL) {
27!
2350
            sixel_helper_set_additional_message(
×
2351
                "sixel_encoder_setopt: sixel_allocator_malloc() failed.");
2352
            status = SIXEL_BAD_ALLOCATION;
×
2353
            goto end;
×
2354
        }
2355
        encoder->color_option = SIXEL_COLOR_OPTION_MAPFILE;
27✔
2356
        break;
27✔
2357
    case SIXEL_OPTFLAG_MONOCHROME:  /* e */
10✔
2358
        encoder->color_option = SIXEL_COLOR_OPTION_MONOCHROME;
15✔
2359
        break;
15✔
2360
    case SIXEL_OPTFLAG_HIGH_COLOR:  /* I */
28✔
2361
        encoder->color_option = SIXEL_COLOR_OPTION_HIGHCOLOR;
42✔
2362
        break;
42✔
2363
    case SIXEL_OPTFLAG_BUILTIN_PALETTE:  /* b */
22✔
2364
        if (strcmp(value, "xterm16") == 0) {
33✔
2365
            encoder->builtin_palette = SIXEL_BUILTIN_XTERM16;
6✔
2366
        } else if (strcmp(value, "xterm256") == 0) {
29✔
2367
            encoder->builtin_palette = SIXEL_BUILTIN_XTERM256;
6✔
2368
        } else if (strcmp(value, "vt340mono") == 0) {
23✔
2369
            encoder->builtin_palette = SIXEL_BUILTIN_VT340_MONO;
3✔
2370
        } else if (strcmp(value, "vt340color") == 0) {
19✔
2371
            encoder->builtin_palette = SIXEL_BUILTIN_VT340_COLOR;
3✔
2372
        } else if (strcmp(value, "gray1") == 0) {
16✔
2373
            encoder->builtin_palette = SIXEL_BUILTIN_G1;
3✔
2374
        } else if (strcmp(value, "gray2") == 0) {
13✔
2375
            encoder->builtin_palette = SIXEL_BUILTIN_G2;
3✔
2376
        } else if (strcmp(value, "gray4") == 0) {
10✔
2377
            encoder->builtin_palette = SIXEL_BUILTIN_G4;
3✔
2378
        } else if (strcmp(value, "gray8") == 0) {
7✔
2379
            encoder->builtin_palette = SIXEL_BUILTIN_G8;
3✔
2380
        } else {
1✔
2381
            sixel_helper_set_additional_message(
3✔
2382
                    "cannot parse builtin palette option.");
2383
            status = SIXEL_BAD_ARGUMENT;
3✔
2384
            goto end;
3✔
2385
        }
2386
        encoder->color_option = SIXEL_COLOR_OPTION_BUILTIN;
30✔
2387
        break;
30✔
2388
    case SIXEL_OPTFLAG_DIFFUSION:  /* d */
46✔
2389
        /* parse --diffusion option */
2390
        if (strcmp(value, "auto") == 0) {
69✔
2391
            encoder->method_for_diffuse = SIXEL_DIFFUSE_AUTO;
3✔
2392
        } else if (strcmp(value, "none") == 0) {
67✔
2393
            encoder->method_for_diffuse = SIXEL_DIFFUSE_NONE;
15✔
2394
        } else if (strcmp(value, "fs") == 0) {
56✔
2395
            encoder->method_for_diffuse = SIXEL_DIFFUSE_FS;
6✔
2396
        } else if (strcmp(value, "atkinson") == 0) {
47✔
2397
            encoder->method_for_diffuse = SIXEL_DIFFUSE_ATKINSON;
12✔
2398
        } else if (strcmp(value, "jajuni") == 0) {
37✔
2399
            encoder->method_for_diffuse = SIXEL_DIFFUSE_JAJUNI;
6✔
2400
        } else if (strcmp(value, "stucki") == 0) {
29✔
2401
            encoder->method_for_diffuse = SIXEL_DIFFUSE_STUCKI;
6✔
2402
        } else if (strcmp(value, "burkes") == 0) {
23✔
2403
            encoder->method_for_diffuse = SIXEL_DIFFUSE_BURKES;
6✔
2404
        } else if (strcmp(value, "sierra1") == 0) {
17!
2405
            encoder->method_for_diffuse = SIXEL_DIFFUSE_SIERRA1;
×
2406
        } else if (strcmp(value, "sierra2") == 0) {
15!
2407
            encoder->method_for_diffuse = SIXEL_DIFFUSE_SIERRA2;
×
2408
        } else if (strcmp(value, "sierra3") == 0) {
15!
2409
            encoder->method_for_diffuse = SIXEL_DIFFUSE_SIERRA3;
×
2410
        } else if (strcmp(value, "a_dither") == 0) {
15✔
2411
            encoder->method_for_diffuse = SIXEL_DIFFUSE_A_DITHER;
6✔
2412
        } else if (strcmp(value, "x_dither") == 0) {
11✔
2413
            encoder->method_for_diffuse = SIXEL_DIFFUSE_X_DITHER;
6✔
2414
        } else if (strcmp(value, "lso1") == 0) {
5!
2415
            encoder->method_for_diffuse = SIXEL_DIFFUSE_LSO1;
×
2416
        } else if (strcmp(value, "lso2") == 0) {
3!
2417
            encoder->method_for_diffuse = SIXEL_DIFFUSE_LSO2;
×
2418
        } else if (strcmp(value, "lso3") == 0) {
3!
2419
            encoder->method_for_diffuse = SIXEL_DIFFUSE_LSO3;
×
2420
        } else {
2421
            sixel_helper_set_additional_message(
3✔
2422
                "specified diffusion method is not supported.");
2423
            status = SIXEL_BAD_ARGUMENT;
3✔
2424
            goto end;
3✔
2425
        }
2426
        break;
66✔
2427
    case SIXEL_OPTFLAG_DIFFUSION_SCAN:  /* y */
2428
        if (strcmp(value, "auto") == 0) {
×
2429
            encoder->method_for_scan = SIXEL_SCAN_AUTO;
×
2430
        } else if (strcmp(value, "serpentine") == 0) {
×
2431
            encoder->method_for_scan = SIXEL_SCAN_SERPENTINE;
×
2432
        } else if (strcmp(value, "raster") == 0) {
×
2433
            encoder->method_for_scan = SIXEL_SCAN_RASTER;
×
2434
        } else {
2435
            sixel_helper_set_additional_message(
×
2436
                "specified diffusion scan is not supported.");
2437
            status = SIXEL_BAD_ARGUMENT;
×
2438
            goto end;
×
2439
        }
2440
        break;
×
2441
    case SIXEL_OPTFLAG_DIFFUSION_CARRY:  /* Y */
2442
        if (strcmp(value, "auto") == 0) {
×
2443
            encoder->method_for_carry = SIXEL_CARRY_AUTO;
×
2444
        } else if (strcmp(value, "direct") == 0) {
×
2445
            encoder->method_for_carry = SIXEL_CARRY_DISABLE;
×
2446
        } else if (strcmp(value, "carry") == 0) {
×
2447
            encoder->method_for_carry = SIXEL_CARRY_ENABLE;
×
2448
        } else {
2449
            sixel_helper_set_additional_message(
×
2450
                "specified diffusion carry mode is not supported.");
2451
            status = SIXEL_BAD_ARGUMENT;
×
2452
            goto end;
×
2453
        }
2454
        break;
×
2455
    case SIXEL_OPTFLAG_FIND_LARGEST:  /* f */
10✔
2456
        /* parse --find-largest option */
2457
        if (value) {
15!
2458
            if (strcmp(value, "auto") == 0) {
15✔
2459
                encoder->method_for_largest = SIXEL_LARGE_AUTO;
3✔
2460
            } else if (strcmp(value, "norm") == 0) {
13✔
2461
                encoder->method_for_largest = SIXEL_LARGE_NORM;
6✔
2462
            } else if (strcmp(value, "lum") == 0) {
8✔
2463
                encoder->method_for_largest = SIXEL_LARGE_LUM;
3✔
2464
            } else {
1✔
2465
                sixel_helper_set_additional_message(
3✔
2466
                    "specified finding method is not supported.");
2467
                status = SIXEL_BAD_ARGUMENT;
3✔
2468
                goto end;
3✔
2469
            }
2470
        }
4✔
2471
        break;
12✔
2472
    case SIXEL_OPTFLAG_SELECT_COLOR:  /* s */
10✔
2473
        /* parse --select-color option */
2474
        if (strcmp(value, "auto") == 0) {
15✔
2475
            encoder->method_for_rep = SIXEL_REP_AUTO;
3✔
2476
        } else if (strcmp(value, "center") == 0) {
13✔
2477
            encoder->method_for_rep = SIXEL_REP_CENTER_BOX;
3✔
2478
        } else if (strcmp(value, "average") == 0) {
10✔
2479
            encoder->method_for_rep = SIXEL_REP_AVERAGE_COLORS;
3✔
2480
        } else if ((strcmp(value, "histogram") == 0) ||
7!
2481
                   (strcmp(value, "histgram") == 0)) {
3!
2482
            encoder->method_for_rep = SIXEL_REP_AVERAGE_PIXELS;
3✔
2483
        } else {
1✔
2484
            sixel_helper_set_additional_message(
3✔
2485
                "specified finding method is not supported.");
2486
            status = SIXEL_BAD_ARGUMENT;
3✔
2487
            goto end;
3✔
2488
        }
2489
        break;
12✔
2490
    case SIXEL_OPTFLAG_CROP:  /* c */
10✔
2491
#if HAVE_SSCANF_S
2492
        number = sscanf_s(value, "%dx%d+%d+%d",
2493
                          &encoder->clipwidth, &encoder->clipheight,
2494
                          &encoder->clipx, &encoder->clipy);
2495
#else
2496
        number = sscanf(value, "%dx%d+%d+%d",
20✔
2497
                        &encoder->clipwidth, &encoder->clipheight,
5✔
2498
                        &encoder->clipx, &encoder->clipy);
5✔
2499
#endif  /* HAVE_SSCANF_S */
2500
        if (number != 4) {
15!
2501
            status = SIXEL_BAD_ARGUMENT;
×
2502
            goto end;
×
2503
        }
2504
        if (encoder->clipwidth <= 0 || encoder->clipheight <= 0) {
15!
2505
            status = SIXEL_BAD_ARGUMENT;
×
2506
            goto end;
×
2507
        }
2508
        if (encoder->clipx < 0 || encoder->clipy < 0) {
15!
2509
            status = SIXEL_BAD_ARGUMENT;
×
2510
            goto end;
×
2511
        }
2512
        encoder->clipfirst = 0;
15✔
2513
        break;
15✔
2514
    case SIXEL_OPTFLAG_WIDTH:  /* w */
50✔
2515
#if HAVE_SSCANF_S
2516
        parsed = sscanf_s(value, "%d%2s", &number, unit, sizeof(unit) - 1);
2517
#else
2518
        parsed = sscanf(value, "%d%2s", &number, unit);
75✔
2519
#endif  /* HAVE_SSCANF_S */
2520
        if (parsed == 2 && strcmp(unit, "%") == 0) {
75!
2521
            encoder->pixelwidth = (-1);
12✔
2522
            encoder->percentwidth = number;
12✔
2523
        } else if (parsed == 1 || (parsed == 2 && strcmp(unit, "px") == 0)) {
67!
2524
            encoder->pixelwidth = number;
51✔
2525
            encoder->percentwidth = (-1);
51✔
2526
        } else if (strcmp(value, "auto") == 0) {
29✔
2527
            encoder->pixelwidth = (-1);
9✔
2528
            encoder->percentwidth = (-1);
9✔
2529
        } else {
3✔
2530
            sixel_helper_set_additional_message(
3✔
2531
                "cannot parse -w/--width option.");
2532
            status = SIXEL_BAD_ARGUMENT;
3✔
2533
            goto end;
3✔
2534
        }
2535
        if (encoder->clipwidth) {
72✔
2536
            encoder->clipfirst = 1;
6✔
2537
        }
2✔
2538
        break;
72✔
2539
    case SIXEL_OPTFLAG_HEIGHT:  /* h */
44✔
2540
#if HAVE_SSCANF_S
2541
        parsed = sscanf_s(value, "%d%2s", &number, unit, sizeof(unit) - 1);
2542
#else
2543
        parsed = sscanf(value, "%d%2s", &number, unit);
66✔
2544
#endif  /* HAVE_SSCANF_S */
2545
        if (parsed == 2 && strcmp(unit, "%") == 0) {
66!
2546
            encoder->pixelheight = (-1);
9✔
2547
            encoder->percentheight = number;
9✔
2548
        } else if (parsed == 1 || (parsed == 2 && strcmp(unit, "px") == 0)) {
60!
2549
            encoder->pixelheight = number;
45✔
2550
            encoder->percentheight = (-1);
45✔
2551
        } else if (strcmp(value, "auto") == 0) {
27✔
2552
            encoder->pixelheight = (-1);
9✔
2553
            encoder->percentheight = (-1);
9✔
2554
        } else {
3✔
2555
            sixel_helper_set_additional_message(
3✔
2556
                "cannot parse -h/--height option.");
2557
            status = SIXEL_BAD_ARGUMENT;
3✔
2558
            goto end;
3✔
2559
        }
2560
        if (encoder->clipheight) {
63✔
2561
            encoder->clipfirst = 1;
3✔
2562
        }
1✔
2563
        break;
63✔
2564
    case SIXEL_OPTFLAG_RESAMPLING:  /* r */
34✔
2565
        /* parse --resampling option */
2566
        if (strcmp(value, "nearest") == 0) {
51✔
2567
            encoder->method_for_resampling = SIXEL_RES_NEAREST;
18✔
2568
        } else if (strcmp(value, "gaussian") == 0) {
39✔
2569
            encoder->method_for_resampling = SIXEL_RES_GAUSSIAN;
3✔
2570
        } else if (strcmp(value, "hanning") == 0) {
31✔
2571
            encoder->method_for_resampling = SIXEL_RES_HANNING;
3✔
2572
        } else if (strcmp(value, "hamming") == 0) {
28✔
2573
            encoder->method_for_resampling = SIXEL_RES_HAMMING;
3✔
2574
        } else if (strcmp(value, "bilinear") == 0) {
25✔
2575
            encoder->method_for_resampling = SIXEL_RES_BILINEAR;
3✔
2576
        } else if (strcmp(value, "welsh") == 0) {
22✔
2577
            encoder->method_for_resampling = SIXEL_RES_WELSH;
3✔
2578
        } else if (strcmp(value, "bicubic") == 0) {
19✔
2579
            encoder->method_for_resampling = SIXEL_RES_BICUBIC;
3✔
2580
        } else if (strcmp(value, "lanczos2") == 0) {
16✔
2581
            encoder->method_for_resampling = SIXEL_RES_LANCZOS2;
6✔
2582
        } else if (strcmp(value, "lanczos3") == 0) {
11✔
2583
            encoder->method_for_resampling = SIXEL_RES_LANCZOS3;
3✔
2584
        } else if (strcmp(value, "lanczos4") == 0) {
7✔
2585
            encoder->method_for_resampling = SIXEL_RES_LANCZOS4;
3✔
2586
        } else {
1✔
2587
            sixel_helper_set_additional_message(
3✔
2588
                "specified desampling method is not supported.");
2589
            status = SIXEL_BAD_ARGUMENT;
3✔
2590
            goto end;
3✔
2591
        }
2592
        break;
48✔
2593
    case SIXEL_OPTFLAG_QUALITY:  /* q */
12✔
2594
        /* parse --quality option */
2595
        if (strcmp(value, "auto") == 0) {
18✔
2596
            encoder->quality_mode = SIXEL_QUALITY_AUTO;
6✔
2597
        } else if (strcmp(value, "high") == 0) {
14✔
2598
            encoder->quality_mode = SIXEL_QUALITY_HIGH;
3✔
2599
        } else if (strcmp(value, "low") == 0) {
10✔
2600
            encoder->quality_mode = SIXEL_QUALITY_LOW;
3✔
2601
        } else if (strcmp(value, "full") == 0) {
7✔
2602
            encoder->quality_mode = SIXEL_QUALITY_FULL;
3✔
2603
        } else {
1✔
2604
            sixel_helper_set_additional_message(
3✔
2605
                "cannot parse quality option.");
2606
            status = SIXEL_BAD_ARGUMENT;
3✔
2607
            goto end;
3✔
2608
        }
2609
        break;
15✔
2610
    case SIXEL_OPTFLAG_LOOPMODE:  /* l */
10✔
2611
        /* parse --loop-control option */
2612
        if (strcmp(value, "auto") == 0) {
15✔
2613
            encoder->loop_mode = SIXEL_LOOP_AUTO;
3✔
2614
        } else if (strcmp(value, "force") == 0) {
13!
2615
            encoder->loop_mode = SIXEL_LOOP_FORCE;
×
2616
        } else if (strcmp(value, "disable") == 0) {
12✔
2617
            encoder->loop_mode = SIXEL_LOOP_DISABLE;
9✔
2618
        } else {
3✔
2619
            sixel_helper_set_additional_message(
3✔
2620
                "cannot parse loop-control option.");
2621
            status = SIXEL_BAD_ARGUMENT;
3✔
2622
            goto end;
3✔
2623
        }
2624
        break;
12✔
2625
    case SIXEL_OPTFLAG_PALETTE_TYPE:  /* t */
18✔
2626
        /* parse --palette-type option */
2627
        if (strcmp(value, "auto") == 0) {
27✔
2628
            encoder->palette_type = SIXEL_PALETTETYPE_AUTO;
6✔
2629
        } else if (strcmp(value, "hls") == 0) {
23✔
2630
            encoder->palette_type = SIXEL_PALETTETYPE_HLS;
15✔
2631
        } else if (strcmp(value, "rgb") == 0) {
11✔
2632
            encoder->palette_type = SIXEL_PALETTETYPE_RGB;
3✔
2633
        } else {
1✔
2634
            sixel_helper_set_additional_message(
3✔
2635
                "cannot parse palette type option.");
2636
            status = SIXEL_BAD_ARGUMENT;
3✔
2637
            goto end;
3✔
2638
        }
2639
        break;
24✔
2640
    case SIXEL_OPTFLAG_BGCOLOR:  /* B */
30✔
2641
        /* parse --bgcolor option */
2642
        if (encoder->bgcolor) {
45✔
2643
            sixel_allocator_free(encoder->allocator, encoder->bgcolor);
6✔
2644
            encoder->bgcolor = NULL;
6✔
2645
        }
2✔
2646
        status = sixel_parse_x_colorspec(&encoder->bgcolor,
60✔
2647
                                         value,
15✔
2648
                                         encoder->allocator);
15✔
2649
        if (SIXEL_FAILED(status)) {
45✔
2650
            sixel_helper_set_additional_message(
21✔
2651
                "cannot parse bgcolor option.");
2652
            status = SIXEL_BAD_ARGUMENT;
21✔
2653
            goto end;
21✔
2654
        }
2655
        break;
24✔
2656
    case SIXEL_OPTFLAG_INSECURE:  /* k */
2657
        encoder->finsecure = 1;
×
2658
        break;
×
2659
    case SIXEL_OPTFLAG_INVERT:  /* i */
4✔
2660
        encoder->finvert = 1;
6✔
2661
        break;
6✔
2662
    case SIXEL_OPTFLAG_USE_MACRO:  /* u */
4✔
2663
        encoder->fuse_macro = 1;
6✔
2664
        break;
6✔
2665
    case SIXEL_OPTFLAG_MACRO_NUMBER:  /* n */
2✔
2666
        encoder->macro_number = atoi(value);
3✔
2667
        if (encoder->macro_number < 0) {
3!
2668
            status = SIXEL_BAD_ARGUMENT;
×
2669
            goto end;
×
2670
        }
2671
        break;
3✔
2672
    case SIXEL_OPTFLAG_IGNORE_DELAY:  /* g */
4✔
2673
        encoder->fignore_delay = 1;
6✔
2674
        break;
6✔
2675
    case SIXEL_OPTFLAG_VERBOSE:  /* v */
6✔
2676
        encoder->verbose = 1;
9✔
2677
        sixel_helper_set_loader_trace(1);
9✔
2678
        break;
9✔
2679
    case SIXEL_OPTFLAG_LOADERS:  /* J */
2680
        if (encoder->loader_order != NULL) {
×
2681
            sixel_allocator_free(encoder->allocator,
×
2682
                                 encoder->loader_order);
×
2683
            encoder->loader_order = NULL;
×
2684
        }
2685
        if (value != NULL && *value != '\0') {
×
2686
            encoder->loader_order = arg_strdup(value,
×
2687
                                               encoder->allocator);
2688
            if (encoder->loader_order == NULL) {
×
2689
                sixel_helper_set_additional_message(
×
2690
                    "sixel_encoder_setopt: "
2691
                    "sixel_allocator_malloc() failed.");
2692
                status = SIXEL_BAD_ALLOCATION;
×
2693
                goto end;
×
2694
            }
2695
        }
2696
        break;
×
2697
    case SIXEL_OPTFLAG_STATIC:  /* S */
2✔
2698
        encoder->fstatic = 1;
3✔
2699
        break;
3✔
2700
    case SIXEL_OPTFLAG_DRCS:  /* @ */
2701
        encoder->fdrcs = 1;
×
2702
        if (strlen(value) == 1 && value[0] >= 32) {
×
2703
            encoder->start_dscs = value[0];
×
2704
        } else {
2705
            sixel_helper_set_additional_message(
×
2706
                "cannot parse DSCS option.");
2707
            status = SIXEL_BAD_ARGUMENT;
×
2708
            goto end;
×
2709
        }
2710
        break;
×
2711
    case SIXEL_OPTFLAG_MAPPING_VERSION:  /* M */
2712
        encoder->drcs_mmv = atoi(value);
×
2713
        if (encoder->drcs_mmv < 0 || encoder->drcs_mmv >= 3) {
×
2714
            sixel_helper_set_additional_message(
×
2715
                "unknown DRCS unicode mapping version.");
2716
            status = SIXEL_BAD_ARGUMENT;
×
2717
            goto end;
×
2718
        }
2719
        break;
×
2720
    case SIXEL_OPTFLAG_PENETRATE:  /* P */
6✔
2721
        encoder->penetrate_multiplexer = 1;
9✔
2722
        break;
9✔
2723
    case SIXEL_OPTFLAG_ENCODE_POLICY:  /* E */
8✔
2724
        if (strcmp(value, "auto") == 0) {
12✔
2725
            encoder->encode_policy = SIXEL_ENCODEPOLICY_AUTO;
3✔
2726
        } else if (strcmp(value, "fast") == 0) {
10✔
2727
            encoder->encode_policy = SIXEL_ENCODEPOLICY_FAST;
3✔
2728
        } else if (strcmp(value, "size") == 0) {
7✔
2729
            encoder->encode_policy = SIXEL_ENCODEPOLICY_SIZE;
3✔
2730
        } else {
1✔
2731
            sixel_helper_set_additional_message(
3✔
2732
                "cannot parse encode policy option.");
2733
            status = SIXEL_BAD_ARGUMENT;
3✔
2734
            goto end;
3✔
2735
        }
2736
        break;
9✔
2737
    case SIXEL_OPTFLAG_LUT_POLICY:  /* L */
2738
        if (strcmp(value, "auto") == 0) {
×
2739
            encoder->lut_policy = SIXEL_LUT_POLICY_AUTO;
×
2740
        } else if (strcmp(value, "5bit") == 0) {
×
2741
            encoder->lut_policy = SIXEL_LUT_POLICY_5BIT;
×
2742
        } else if (strcmp(value, "6bit") == 0) {
×
2743
            encoder->lut_policy = SIXEL_LUT_POLICY_6BIT;
×
2744
        } else if (strcmp(value, "robinhood") == 0) {
×
2745
            encoder->lut_policy = SIXEL_LUT_POLICY_ROBINHOOD;
×
2746
        } else if (strcmp(value, "hopscotch") == 0) {
×
2747
            encoder->lut_policy = SIXEL_LUT_POLICY_HOPSCOTCH;
×
2748
        } else {
2749
            sixel_helper_set_additional_message(
×
2750
                "cannot parse lut policy option.");
2751
            status = SIXEL_BAD_ARGUMENT;
×
2752
            goto end;
×
2753
        }
2754
        if (encoder->dither_cache != NULL) {
×
2755
            sixel_dither_set_lut_policy(encoder->dither_cache,
×
2756
                                        encoder->lut_policy);
2757
        }
2758
        break;
×
2759
    case SIXEL_OPTFLAG_WORKING_COLORSPACE:  /* W */
2760
        if (value == NULL) {
×
2761
            sixel_helper_set_additional_message(
×
2762
                "working-colorspace requires an argument.");
2763
            status = SIXEL_BAD_ARGUMENT;
×
2764
            goto end;
×
2765
        } else {
2766
            len = strlen(value);
×
2767

2768
            if (len >= sizeof(lowered)) {
×
2769
                sixel_helper_set_additional_message(
×
2770
                    "specified working colorspace name is too long.");
2771
                status = SIXEL_BAD_ARGUMENT;
×
2772
                goto end;
×
2773
            }
2774
            for (i = 0; i < len; ++i) {
×
2775
                lowered[i] = (char)tolower((unsigned char)value[i]);
×
2776
            }
2777
            lowered[len] = '\0';
×
2778

2779
            if (strcmp(lowered, "gamma") == 0) {
×
2780
                encoder->working_colorspace = SIXEL_COLORSPACE_GAMMA;
×
2781
            } else if (strcmp(lowered, "linear") == 0) {
×
2782
                encoder->working_colorspace = SIXEL_COLORSPACE_LINEAR;
×
2783
            } else if (strcmp(lowered, "oklab") == 0) {
×
2784
                encoder->working_colorspace = SIXEL_COLORSPACE_OKLAB;
×
2785
            } else {
2786
                sixel_helper_set_additional_message(
×
2787
                    "unsupported working colorspace specified.");
2788
                status = SIXEL_BAD_ARGUMENT;
×
2789
                goto end;
×
2790
            }
2791
        }
2792
        break;
×
2793
    case SIXEL_OPTFLAG_OUTPUT_COLORSPACE:  /* U */
2794
        if (value == NULL) {
×
2795
            sixel_helper_set_additional_message(
×
2796
                "output-colorspace requires an argument.");
2797
            status = SIXEL_BAD_ARGUMENT;
×
2798
            goto end;
×
2799
        } else {
2800
            len = strlen(value);
×
2801

2802
            if (len >= sizeof(lowered)) {
×
2803
                sixel_helper_set_additional_message(
×
2804
                    "specified output colorspace name is too long.");
2805
                status = SIXEL_BAD_ARGUMENT;
×
2806
                goto end;
×
2807
            }
2808
            for (i = 0; i < len; ++i) {
×
2809
                lowered[i] = (char)tolower((unsigned char)value[i]);
×
2810
            }
2811
            lowered[len] = '\0';
×
2812

2813
            if (strcmp(lowered, "gamma") == 0) {
×
2814
                encoder->output_colorspace = SIXEL_COLORSPACE_GAMMA;
×
2815
            } else if (strcmp(lowered, "linear") == 0) {
×
2816
                encoder->output_colorspace = SIXEL_COLORSPACE_LINEAR;
×
2817
            } else if (strcmp(lowered, "smpte-c") == 0 ||
×
2818
                       strcmp(lowered, "smptec") == 0) {
×
2819
                encoder->output_colorspace = SIXEL_COLORSPACE_SMPTEC;
×
2820
            } else {
2821
                sixel_helper_set_additional_message(
×
2822
                    "unsupported output colorspace specified.");
2823
                status = SIXEL_BAD_ARGUMENT;
×
2824
                goto end;
×
2825
            }
2826
        }
2827
        break;
×
2828
    case SIXEL_OPTFLAG_ORMODE:  /* O */
2829
        encoder->ormode = 1;
×
2830
        break;
×
2831
    case SIXEL_OPTFLAG_COMPLEXION_SCORE:  /* C */
6✔
2832
        encoder->complexion = atoi(value);
9✔
2833
        if (encoder->complexion < 1) {
9✔
2834
            sixel_helper_set_additional_message(
3✔
2835
                "complexion parameter must be 1 or more.");
2836
            status = SIXEL_BAD_ARGUMENT;
3✔
2837
            goto end;
3✔
2838
        }
2839
        break;
6✔
2840
    case SIXEL_OPTFLAG_PIPE_MODE:  /* D */
2841
        encoder->pipe_mode = 1;
×
2842
        break;
×
2843
    case '?':  /* unknown option */
3✔
2844
    default:
2845
        /* exit if unknown options are specified */
2846
        sixel_helper_set_additional_message(
3✔
2847
            "unknown option is specified.");
2848
        status = SIXEL_BAD_ARGUMENT;
3✔
2849
        goto end;
3✔
2850
    }
2851

2852
    /* detects arguments conflictions */
2853
    if (encoder->reqcolors != (-1)) {
612✔
2854
        switch (encoder->color_option) {
99!
2855
        case SIXEL_COLOR_OPTION_MAPFILE:
2856
            sixel_helper_set_additional_message(
×
2857
                "option -p, --colors conflicts with -m, --mapfile.");
2858
            status = SIXEL_BAD_ARGUMENT;
×
2859
            goto end;
×
2860
        case SIXEL_COLOR_OPTION_MONOCHROME:
2✔
2861
            sixel_helper_set_additional_message(
3✔
2862
                "option -e, --monochrome conflicts with -p, --colors.");
2863
            status = SIXEL_BAD_ARGUMENT;
3✔
2864
            goto end;
3✔
2865
        case SIXEL_COLOR_OPTION_HIGHCOLOR:
2✔
2866
            sixel_helper_set_additional_message(
3✔
2867
                "option -p, --colors conflicts with -I, --high-color.");
2868
            status = SIXEL_BAD_ARGUMENT;
3✔
2869
            goto end;
3✔
2870
        case SIXEL_COLOR_OPTION_BUILTIN:
2✔
2871
            sixel_helper_set_additional_message(
3✔
2872
                "option -p, --colors conflicts with -b, --builtin-palette.");
2873
            status = SIXEL_BAD_ARGUMENT;
3✔
2874
            goto end;
3✔
2875
        default:
60✔
2876
            break;
90✔
2877
        }
2878
    }
30✔
2879

2880
    /* 8bit output option(-8) conflicts width GNU Screen integration(-P) */
2881
    if (encoder->f8bit && encoder->penetrate_multiplexer) {
603✔
2882
        sixel_helper_set_additional_message(
3✔
2883
            "option -8 --8bit-mode conflicts"
2884
            " with -P, --penetrate.");
2885
        status = SIXEL_BAD_ARGUMENT;
3✔
2886
        goto end;
3✔
2887
    }
2888

2889
    status = SIXEL_OK;
600✔
2890

2891
end:
448✔
2892
    sixel_encoder_unref(encoder);
672✔
2893

2894
    return status;
672✔
2895
}
2896

2897

2898
/* called when image loader component load a image frame */
2899
static SIXELSTATUS
2900
load_image_callback(sixel_frame_t *frame, void *data)
518✔
2901
{
2902
    return sixel_encoder_encode_frame((sixel_encoder_t *)data, frame, NULL);
518✔
2903
}
2904

2905

2906
/* load source data from specified file and encode it to SIXEL format
2907
 * output to encoder->outfd */
2908
SIXELAPI SIXELSTATUS
2909
sixel_encoder_encode(
417✔
2910
    sixel_encoder_t *encoder,   /* encoder object */
2911
    char const      *filename)  /* input filename */
2912
{
2913
    SIXELSTATUS status = SIXEL_FALSE;
417✔
2914
    int fuse_palette = 1;
417✔
2915
    sixel_loader_t *loader;
2916

2917
    if (encoder == NULL) {
417!
2918
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2919
#  pragma GCC diagnostic push
2920
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2921
#endif
2922
        encoder = sixel_encoder_create();
×
2923
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
2924
#  pragma GCC diagnostic pop
2925
#endif
2926
        if (encoder == NULL) {
×
2927
            sixel_helper_set_additional_message(
×
2928
                "sixel_encoder_encode: sixel_encoder_create() failed.");
2929
            status = SIXEL_BAD_ALLOCATION;
×
2930
            goto end;
×
2931
        }
2932
    } else {
2933
        sixel_encoder_ref(encoder);
417✔
2934
    }
2935

2936
    /* if required color is not set, set the max value */
2937
    if (encoder->reqcolors == (-1)) {
417✔
2938
        encoder->reqcolors = SIXEL_PALETTE_MAX;
399✔
2939
    }
133✔
2940

2941
    /* if required color is less then 2, set the min value */
2942
    if (encoder->reqcolors < 2) {
417✔
2943
        encoder->reqcolors = SIXEL_PALETTE_MIN;
3✔
2944
    }
1✔
2945

2946
    /* if color space option is not set, choose RGB color space */
2947
    if (encoder->palette_type == SIXEL_PALETTETYPE_AUTO) {
417✔
2948
        encoder->palette_type = SIXEL_PALETTETYPE_RGB;
399✔
2949
    }
133✔
2950

2951
    /* if color option is not default value, prohibit to read
2952
       the file as a paletted image */
2953
    if (encoder->color_option != SIXEL_COLOR_OPTION_DEFAULT) {
417✔
2954
        fuse_palette = 0;
99✔
2955
    }
33✔
2956

2957
    /* if scaling options are set, prohibit to read the file as
2958
       a paletted image */
2959
    if (encoder->percentwidth > 0 ||
533✔
2960
        encoder->percentheight > 0 ||
405✔
2961
        encoder->pixelwidth > 0 ||
399✔
2962
        encoder->pixelheight > 0) {
381✔
2963
        fuse_palette = 0;
99✔
2964
    }
33✔
2965

2966
reload:
278✔
2967
    loader = NULL;
417✔
2968

2969
    sixel_helper_set_loader_trace(encoder->verbose);
417✔
2970
    sixel_helper_set_thumbnail_size_hint(
417✔
2971
        sixel_encoder_thumbnail_hint(encoder));
139✔
2972

2973
    status = sixel_loader_new(&loader, encoder->allocator);
417✔
2974
    if (SIXEL_FAILED(status)) {
417!
NEW
2975
        goto load_end;
×
2976
    }
2977

2978
    status = sixel_loader_setopt(loader,
556✔
2979
                                 SIXEL_LOADER_OPTION_REQUIRE_STATIC,
2980
                                 &encoder->fstatic);
417✔
2981
    if (SIXEL_FAILED(status)) {
417!
NEW
2982
        goto load_end;
×
2983
    }
2984

2985
    status = sixel_loader_setopt(loader,
417✔
2986
                                 SIXEL_LOADER_OPTION_USE_PALETTE,
2987
                                 &fuse_palette);
2988
    if (SIXEL_FAILED(status)) {
417!
NEW
2989
        goto load_end;
×
2990
    }
2991

2992
    status = sixel_loader_setopt(loader,
556✔
2993
                                 SIXEL_LOADER_OPTION_REQCOLORS,
2994
                                 &encoder->reqcolors);
417✔
2995
    if (SIXEL_FAILED(status)) {
417!
NEW
2996
        goto load_end;
×
2997
    }
2998

2999
    status = sixel_loader_setopt(loader,
556✔
3000
                                 SIXEL_LOADER_OPTION_BGCOLOR,
3001
                                 encoder->bgcolor);
417✔
3002
    if (SIXEL_FAILED(status)) {
417!
NEW
3003
        goto load_end;
×
3004
    }
3005

3006
    status = sixel_loader_setopt(loader,
556✔
3007
                                 SIXEL_LOADER_OPTION_LOOP_CONTROL,
3008
                                 &encoder->loop_mode);
417✔
3009
    if (SIXEL_FAILED(status)) {
417!
NEW
3010
        goto load_end;
×
3011
    }
3012

3013
    status = sixel_loader_setopt(loader,
556✔
3014
                                 SIXEL_LOADER_OPTION_INSECURE,
3015
                                 &encoder->finsecure);
417✔
3016
    if (SIXEL_FAILED(status)) {
417!
NEW
3017
        goto load_end;
×
3018
    }
3019

3020
    status = sixel_loader_setopt(loader,
556✔
3021
                                 SIXEL_LOADER_OPTION_CANCEL_FLAG,
3022
                                 encoder->cancel_flag);
417✔
3023
    if (SIXEL_FAILED(status)) {
417!
NEW
3024
        goto load_end;
×
3025
    }
3026

3027
    status = sixel_loader_setopt(loader,
556✔
3028
                                 SIXEL_LOADER_OPTION_LOADER_ORDER,
3029
                                 encoder->loader_order);
417✔
3030
    if (SIXEL_FAILED(status)) {
417!
NEW
3031
        goto load_end;
×
3032
    }
3033

3034
    status = sixel_loader_setopt(loader,
556✔
3035
                                 SIXEL_LOADER_OPTION_CONTEXT,
3036
                                 encoder);
139✔
3037
    if (SIXEL_FAILED(status)) {
417!
NEW
3038
        goto load_end;
×
3039
    }
3040

3041
    status = sixel_loader_load_file(loader,
556✔
3042
                                    filename,
139✔
3043
                                    load_image_callback);
3044
    if (status != SIXEL_OK) {
417✔
3045
        goto load_end;
21✔
3046
    }
3047

3048
load_end:
264✔
3049
    sixel_loader_unref(loader);
417✔
3050
    loader = NULL;
417✔
3051

3052
    if (status != SIXEL_OK) {
417✔
3053
        goto end;
21✔
3054
    }
3055

3056
    if (encoder->pipe_mode) {
396!
3057
#if HAVE_CLEARERR
3058
        clearerr(stdin);
×
3059
#endif  /* HAVE_FSEEK */
3060
        while (encoder->cancel_flag && !*encoder->cancel_flag) {
×
3061
            status = sixel_tty_wait_stdin(1000000);
×
3062
            if (SIXEL_FAILED(status)) {
×
3063
                goto end;
×
3064
            }
3065
            if (status != SIXEL_OK) {
×
3066
                break;
×
3067
            }
3068
        }
3069
        if (!encoder->cancel_flag || !*encoder->cancel_flag) {
×
3070
            goto reload;
×
3071
        }
3072
    }
3073

3074
    /* the status may not be SIXEL_OK */
3075

3076
end:
264✔
3077
    sixel_encoder_unref(encoder);
417✔
3078

3079
    return status;
417✔
3080
}
3081

3082

3083
/* encode specified pixel data to SIXEL format
3084
 * output to encoder->outfd */
3085
SIXELAPI SIXELSTATUS
3086
sixel_encoder_encode_bytes(
×
3087
    sixel_encoder_t     /* in */    *encoder,
3088
    unsigned char       /* in */    *bytes,
3089
    int                 /* in */    width,
3090
    int                 /* in */    height,
3091
    int                 /* in */    pixelformat,
3092
    unsigned char       /* in */    *palette,
3093
    int                 /* in */    ncolors)
3094
{
3095
    SIXELSTATUS status = SIXEL_FALSE;
×
3096
    sixel_frame_t *frame = NULL;
×
3097

3098
    if (encoder == NULL || bytes == NULL) {
×
3099
        status = SIXEL_BAD_ARGUMENT;
×
3100
        goto end;
×
3101
    }
3102

3103
    status = sixel_frame_new(&frame, encoder->allocator);
×
3104
    if (SIXEL_FAILED(status)) {
×
3105
        goto end;
×
3106
    }
3107

3108
    status = sixel_frame_init(frame, bytes, width, height,
×
3109
                              pixelformat, palette, ncolors);
3110
    if (SIXEL_FAILED(status)) {
×
3111
        goto end;
×
3112
    }
3113

3114
    status = sixel_encoder_encode_frame(encoder, frame, NULL);
×
3115
    if (SIXEL_FAILED(status)) {
×
3116
        goto end;
×
3117
    }
3118

3119
    status = SIXEL_OK;
×
3120

3121
end:
3122
    /* we need to free the frame before exiting, but we can't use the
3123
       sixel_frame_destroy function, because that will also attempt to
3124
       free the pixels and palette, which we don't own */
3125
    if (frame != NULL && encoder->allocator != NULL) {
×
3126
        sixel_allocator_free(encoder->allocator, frame);
×
3127
        sixel_allocator_unref(encoder->allocator);
×
3128
    }
3129
    return status;
×
3130
}
3131

3132

3133
#if HAVE_TESTS
3134
static int
3135
test1(void)
×
3136
{
3137
    int nret = EXIT_FAILURE;
×
3138
    sixel_encoder_t *encoder = NULL;
×
3139

3140
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3141
#  pragma GCC diagnostic push
3142
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
3143
#endif
3144
    encoder = sixel_encoder_create();
×
3145
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3146
#  pragma GCC diagnostic pop
3147
#endif
3148
    if (encoder == NULL) {
×
3149
        goto error;
×
3150
    }
3151
    sixel_encoder_ref(encoder);
×
3152
    sixel_encoder_unref(encoder);
×
3153
    nret = EXIT_SUCCESS;
×
3154

3155
error:
3156
    sixel_encoder_unref(encoder);
×
3157
    return nret;
×
3158
}
3159

3160

3161
static int
3162
test2(void)
×
3163
{
3164
    int nret = EXIT_FAILURE;
×
3165
    SIXELSTATUS status;
3166
    sixel_encoder_t *encoder = NULL;
×
3167
    sixel_frame_t *frame = NULL;
×
3168
    unsigned char *buffer;
3169
    int height = 0;
×
3170
    int is_animation = 0;
×
3171

3172
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3173
#  pragma GCC diagnostic push
3174
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
3175
#endif
3176
    encoder = sixel_encoder_create();
×
3177
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3178
#  pragma GCC diagnostic pop
3179
#endif
3180
    if (encoder == NULL) {
×
3181
        goto error;
×
3182
    }
3183

3184
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3185
#  pragma GCC diagnostic push
3186
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
3187
#endif
3188
    frame = sixel_frame_create();
×
3189
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3190
#  pragma GCC diagnostic pop
3191
#endif
3192
    if (encoder == NULL) {
×
3193
        goto error;
×
3194
    }
3195

3196
    buffer = (unsigned char *)sixel_allocator_malloc(encoder->allocator, 3);
×
3197
    if (buffer == NULL) {
×
3198
        goto error;
×
3199
    }
3200
    status = sixel_frame_init(frame, buffer, 1, 1,
×
3201
                              SIXEL_PIXELFORMAT_RGB888,
3202
                              NULL, 0);
3203
    if (SIXEL_FAILED(status)) {
×
3204
        goto error;
×
3205
    }
3206

3207
    if (sixel_frame_get_loop_no(frame) != 0 || sixel_frame_get_frame_no(frame) != 0) {
×
3208
        is_animation = 1;
×
3209
    }
3210

3211
    height = sixel_frame_get_height(frame);
×
3212

3213
    status = sixel_tty_scroll(sixel_write_callback, encoder->outfd, height, is_animation);
×
3214
    if (SIXEL_FAILED(status)) {
×
3215
        goto error;
×
3216
    }
3217

3218
    nret = EXIT_SUCCESS;
×
3219

3220
error:
3221
    sixel_encoder_unref(encoder);
×
3222
    sixel_frame_unref(frame);
×
3223
    return nret;
×
3224
}
3225

3226

3227
static int
3228
test3(void)
×
3229
{
3230
    int nret = EXIT_FAILURE;
×
3231
    int result;
3232

3233
    result = sixel_tty_wait_stdin(1000);
×
3234
    if (result != 0) {
×
3235
        goto error;
×
3236
    }
3237

3238
    nret = EXIT_SUCCESS;
×
3239

3240
error:
3241
    return nret;
×
3242
}
3243

3244

3245
static int
3246
test4(void)
×
3247
{
3248
    int nret = EXIT_FAILURE;
×
3249
    sixel_encoder_t *encoder = NULL;
×
3250
    SIXELSTATUS status;
3251

3252
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3253
# pragma GCC diagnostic push
3254
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
3255
#endif
3256
    encoder = sixel_encoder_create();
×
3257
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
3258
# pragma GCC diagnostic pop
3259
#endif
3260
    if (encoder == NULL) {
×
3261
        goto error;
×
3262
    }
3263

3264
    status = sixel_encoder_setopt(encoder,
×
3265
                                  SIXEL_OPTFLAG_LOOPMODE,
3266
                                  "force");
3267
    if (SIXEL_FAILED(status)) {
×
3268
        goto error;
×
3269
    }
3270

3271
    status = sixel_encoder_setopt(encoder,
×
3272
                                  SIXEL_OPTFLAG_PIPE_MODE,
3273
                                  "force");
3274
    if (SIXEL_FAILED(status)) {
×
3275
        goto error;
×
3276
    }
3277

3278
    nret = EXIT_SUCCESS;
×
3279

3280
error:
3281
    sixel_encoder_unref(encoder);
×
3282
    return nret;
×
3283
}
3284

3285

3286
static int
3287
test5(void)
×
3288
{
3289
    int nret = EXIT_FAILURE;
×
3290
    sixel_encoder_t *encoder = NULL;
×
3291
    sixel_allocator_t *allocator = NULL;
×
3292
    SIXELSTATUS status;
3293

3294
    status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
×
3295
    if (SIXEL_FAILED(status)) {
×
3296
        goto error;
×
3297
    }
3298

3299
    status = sixel_encoder_new(&encoder, allocator);
×
3300
    if (SIXEL_FAILED(status)) {
×
3301
        goto error;
×
3302
    }
3303

3304
    sixel_encoder_ref(encoder);
×
3305
    sixel_encoder_unref(encoder);
×
3306
    nret = EXIT_SUCCESS;
×
3307

3308
error:
3309
    sixel_encoder_unref(encoder);
×
3310
    return nret;
×
3311
}
3312

3313

3314
SIXELAPI int
3315
sixel_encoder_tests_main(void)
×
3316
{
3317
    int nret = EXIT_FAILURE;
×
3318
    size_t i;
3319
    typedef int (* testcase)(void);
3320

3321
    static testcase const testcases[] = {
3322
        test1,
3323
        test2,
3324
        test3,
3325
        test4,
3326
        test5
3327
    };
3328

3329
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
3330
        nret = testcases[i]();
×
3331
        if (nret != EXIT_SUCCESS) {
×
3332
            goto error;
×
3333
        }
3334
    }
3335

3336
    nret = EXIT_SUCCESS;
×
3337

3338
error:
3339
    return nret;
×
3340
}
3341
#endif  /* HAVE_TESTS */
3342

3343

3344
/* emacs Local Variables:      */
3345
/* emacs mode: c               */
3346
/* emacs tab-width: 4          */
3347
/* emacs indent-tabs-mode: nil */
3348
/* emacs c-basic-offset: 4     */
3349
/* emacs End:                  */
3350
/* vim: set expandtab ts=4 : */
3351
/* 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