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

saitoha / libsixel / 20114228509

10 Dec 2025 09:44PM UTC coverage: 51.593% (+6.9%) from 44.65%
20114228509

push

github

saitoha
tests: temporarily skip oversized python error scenario

11613 of 40017 branches covered (29.02%)

19771 of 38321 relevant lines covered (51.59%)

4030622.38 hits per line

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

86.92
/src/loader-builtin.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2021-2025 libsixel developers. See `AUTHORS`.
5
 * Copyright (c) 2014-2019 Hayaki Saito
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to
9
 * deal in the Software without restriction, including without limitation the
10
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
 * sell copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
 * FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
 * DEALINGS IN THE SOFTWARE.
24
 *
25
 * Builtin loader covering SIXEL, PNM, GIF, and stb_image fallbacks.  This
26
 * module keeps the heavyweight stb_image implementation isolated from the
27
 * registry so other backends avoid pulling in its macros and includes.
28
 */
29

30
#include "config.h"
31

32
/* STDC_HEADERS */
33
#include <stdio.h>
34
#include <stdlib.h>
35

36
#if HAVE_STRING_H
37
# include <string.h>
38
#endif
39
#if HAVE_CTYPE_H
40
# include <ctype.h>
41
#endif
42
#if HAVE_LIMITS_H
43
# include <limits.h>
44
#endif
45

46
#include <sixel.h>
47

48
#include "allocator.h"
49
#include "chunk.h"
50
#include "frame.h"
51
#include "fromgif.h"
52
#include "frompnm.h"
53
#include "pixelformat.h"
54
#include "loader-builtin.h"
55
#include "loader-common.h"
56
#include "loader.h"
57

58
static sixel_allocator_t *stbi_allocator;
59

60
void *
61
stbi_malloc(size_t n)
1,714✔
62
{
63
    return sixel_allocator_malloc(stbi_allocator, n);
1,714✔
64
}
65

66
void *
67
stbi_realloc(void *p, size_t n)
612✔
68
{
69
    return sixel_allocator_realloc(stbi_allocator, p, n);
612✔
70
}
71

72
void
73
stbi_free(void *p)
2,433✔
74
{
75
    sixel_allocator_free(stbi_allocator, p);
2,277✔
76
}
1,725✔
77

78
#define STBI_MALLOC stbi_malloc
79
#define STBI_REALLOC stbi_realloc
80
#define STBI_FREE stbi_free
81

82
#define STBI_NO_STDIO 1
83
#define STB_IMAGE_IMPLEMENTATION 1
84
#define STBI_FAILURE_USERMSG 1
85
#if defined(_WIN32)
86
# define STBI_NO_THREAD_LOCALS 1  /* no tls */
87
#endif
88
#define STBI_NO_GIF
89
#define STBI_NO_PNM
90

91
#if HAVE_DIAGNOSTIC_SIGN_CONVERSION
92
# pragma GCC diagnostic push
93
# pragma GCC diagnostic ignored "-Wsign-conversion"
94
#endif
95
#if HAVE_DIAGNOSTIC_STRICT_OVERFLOW
96
# pragma GCC diagnostic push
97
# pragma GCC diagnostic ignored "-Wstrict-overflow"
98
#endif
99
#if HAVE_DIAGNOSTIC_SWITCH_DEFAULT
100
# pragma GCC diagnostic push
101
# pragma GCC diagnostic ignored "-Wswitch-default"
102
#endif
103
#if HAVE_DIAGNOSTIC_SHADOW
104
# pragma GCC diagnostic push
105
# pragma GCC diagnostic ignored "-Wshadow"
106
#endif
107
#if HAVE_DIAGNOSTIC_DOUBLE_PROMOTION
108
# pragma GCC diagnostic push
109
# pragma GCC diagnostic ignored "-Wdouble-promotion"
110
#endif
111
#if HAVE_DIAGNOSTIC_UNUSED_FUNCTION
112
# pragma GCC diagnostic push
113
# pragma GCC diagnostic ignored "-Wunused-function"
114
#endif
115
#if HAVE_DIAGNOSTIC_UNUSED_BUT_SET_VARIABLE
116
# pragma GCC diagnostic push
117
# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
118
#endif
119
#include "stb_image.h"
120
#if HAVE_DIAGNOSTIC_UNUSED_BUT_SET_VARIABLE
121
# pragma GCC diagnostic pop
122
#endif
123
#if HAVE_DIAGNOSTIC_UNUSED_FUNCTION
124
# pragma GCC diagnostic pop
125
#endif
126
#if HAVE_DIAGNOSTIC_DOUBLE_PROMOTION
127
# pragma GCC diagnostic pop
128
#endif
129
#if HAVE_DIAGNOSTIC_SHADOW
130
# pragma GCC diagnostic pop
131
#endif
132
#if HAVE_DIAGNOSTIC_SWITCH_DEFAULT
133
# pragma GCC diagnostic pop
134
#endif
135
#if HAVE_DIAGNOSTIC_STRICT_OVERFLOW
136
# pragma GCC diagnostic pop
137
#endif
138
#if HAVE_DIAGNOSTIC_SIGN_CONVERSION
139
# pragma GCC diagnostic pop
140
#endif
141

142
static SIXELSTATUS
143
load_sixel(unsigned char        /* out */ **result,
209✔
144
           unsigned char        /* in */  *buffer,
145
           int                  /* in */  size,
146
           int                  /* out */ *psx,
147
           int                  /* out */ *psy,
148
           unsigned char        /* out */ **ppalette,
149
           int                  /* out */ *pncolors,
150
           int                  /* in */  reqcolors,
151
           int                  /* out */ *ppixelformat,
152
           sixel_allocator_t    /* in */  *allocator)
153
{
154
    SIXELSTATUS status;
209✔
155
    unsigned char *decoded_pixels;
209✔
156
    unsigned char *decoded_palette;
209✔
157
    size_t image_bytes;
209✔
158

159
    if (result == NULL || pncolors == NULL || ppixelformat == NULL ||
209!
160
            allocator == NULL) {
209!
161
        return SIXEL_BAD_ARGUMENT;
162
    }
163

164
    status = SIXEL_FALSE;
209✔
165
    decoded_pixels = NULL;
209✔
166
    decoded_palette = NULL;
209✔
167
    image_bytes = 0;
209✔
168

169
    status = sixel_decode_raw(buffer,
262✔
170
                              size,
53✔
171
                              &decoded_pixels,
172
                              psx,
53✔
173
                              psy,
53✔
174
                              &decoded_palette,
175
                              pncolors,
53✔
176
                              allocator);
53✔
177
    if (loader_trace_is_enabled()) {
209✔
178
        loader_trace_message("load_sixel: sixel_decode_raw -> %d",
4✔
179
                             (int)status);
1✔
180
    }
1✔
181
    if (SIXEL_FAILED(status)) {
209!
182
        goto end;
×
183
    }
184

185
    image_bytes = (size_t)(*psx) * (size_t)(*psy);
209✔
186

187
    if (ppalette == NULL ||
250!
188
            (reqcolors > 0 && *pncolors > reqcolors)) {
161!
189
        size_t rgb_bytes;
48✔
190
        size_t index;
48✔
191

192
        if (decoded_palette == NULL) {
48!
193
            status = SIXEL_BAD_INPUT;
×
194
            goto end;
×
195
        }
196
        rgb_bytes = image_bytes * 3u;
48✔
197
        *result = sixel_allocator_malloc(allocator, rgb_bytes);
48✔
198
        if (*result == NULL) {
48!
199
            sixel_helper_set_additional_message(
×
200
                "load_sixel: sixel_allocator_malloc() failed.");
201
            status = SIXEL_BAD_ALLOCATION;
×
202
            goto end;
×
203
        }
204
        for (index = 0; index < image_bytes; ++index) {
7,560,768✔
205
            size_t palette_index;
7,560,720✔
206
            size_t pixel_offset;
7,560,720✔
207

208
            palette_index = decoded_pixels[index] * 3u;
7,560,720✔
209
            pixel_offset = index * 3u;
7,560,720✔
210
            (*result)[pixel_offset + 0] = decoded_palette[palette_index + 0];
7,560,720✔
211
            (*result)[pixel_offset + 1] = decoded_palette[palette_index + 1];
7,560,720✔
212
            (*result)[pixel_offset + 2] = decoded_palette[palette_index + 2];
7,560,720✔
213
        }
1,890,180✔
214
        *ppixelformat = SIXEL_PIXELFORMAT_RGB888;
48✔
215
    } else {
12!
216
        *result = decoded_pixels;
161✔
217
        decoded_pixels = NULL;
161✔
218
        *ppalette = decoded_palette;
161✔
219
        decoded_palette = NULL;
161✔
220
        *ppixelformat = SIXEL_PIXELFORMAT_PAL8;
161✔
221
    }
222
    status = SIXEL_OK;
53✔
223

224
end:
156✔
225
    if (decoded_pixels != NULL) {
209✔
226
        sixel_allocator_free(allocator, decoded_pixels);
48✔
227
        decoded_pixels = NULL;
48✔
228
    }
12✔
229
    if (decoded_palette != NULL) {
209✔
230
        sixel_allocator_free(allocator, decoded_palette);
48✔
231
        decoded_palette = NULL;
48✔
232
    }
12✔
233

234
    return status;
53✔
235
}
53✔
236

237
static int
238
chunk_is_sixel(sixel_chunk_t const *chunk)
684✔
239
{
240
    unsigned char *p;
684✔
241
    unsigned char *end;
684✔
242

243
    p = chunk->buffer;
684✔
244
    end = p + chunk->size;
684✔
245

246
    if (chunk->size < 3) {
684!
247
        return 0;
248
    }
249

250
    p++;
681✔
251
    if (p >= end) {
681!
252
        return 0;
253
    }
254
    if (*(p - 1) == 0x90 || (*(p - 1) == 0x1b && *p == 0x50)) {
681!
255
        while (p++ < end) {
761!
256
            if (*p == 0x71) {
761✔
257
                return 1;
53✔
258
            } else if (*p == 0x18 || *p == 0x1a) {
552!
259
                return 0;
260
            } else if (*p < 0x20) {
552✔
261
                continue;
8✔
262
            } else if (*p < 0x30) {
544!
263
                return 0;
264
            } else if (*p < 0x40) {
136✔
265
                continue;
135✔
266
            }
267
        }
268
    }
269
    return 0;
31✔
270
}
84✔
271

272
static int
273
chunk_is_pnm(sixel_chunk_t const *chunk)
475✔
274
{
275
    if (chunk->size < 2) {
475!
276
        return 0;
277
    }
278
    if (chunk->buffer[0] == 'P' &&
483✔
279
        chunk->buffer[1] >= '1' &&
45!
280
        chunk->buffer[1] <= '6') {
11✔
281
        return 1;
40✔
282
    }
283
    return 0;
21✔
284
}
31✔
285

286
typedef union _fn_pointer {
287
    sixel_load_image_function fn;
288
    void *                    p;
289
} fn_pointer;
290

291
SIXELSTATUS
292
load_with_builtin(
684✔
293
    sixel_chunk_t const *pchunk,
294
    int fstatic,
295
    int fuse_palette,
296
    int reqcolors,
297
    unsigned char *bgcolor,
298
    int loop_control,
299
    sixel_load_image_function fn_load,
300
    void *context)
301
{
302
    SIXELSTATUS status;
684✔
303
    unsigned char *pixels;
684✔
304
    int depth;
684✔
305
    sixel_frame_t *frame;
684✔
306
    fn_pointer fnp;
684✔
307
    stbi__context stb_context;
684✔
308
    char message[80];
684✔
309
    int nwrite;
684✔
310

311
    status = SIXEL_BAD_INPUT;
684✔
312
    pixels = NULL;
684✔
313
    depth = 0;
684✔
314
    frame = NULL;
684✔
315
    fnp.fn = NULL;
684✔
316
    stb_context = (stbi__context){ 0 };
684✔
317
    nwrite = 0;
684✔
318

319
    if (pchunk == NULL) {
684!
320
        status = SIXEL_BAD_ARGUMENT;
×
321
        goto end;
×
322
    }
323

324
    if (chunk_is_sixel(pchunk)) {
684✔
325
        status = sixel_frame_new(&frame, pchunk->allocator);
209✔
326
        if (SIXEL_FAILED(status)) {
209!
327
            goto end;
×
328
        }
329
        if (pchunk->size > INT_MAX) {
209!
330
            status = SIXEL_BAD_INTEGER_OVERFLOW;
×
331
            goto end;
×
332
        }
333
        status = load_sixel(&pixels,
173✔
334
                            pchunk->buffer,
209✔
335
                            (int)pchunk->size,
53✔
336
                            &frame->width,
53✔
337
                            &frame->height,
53✔
338
                            fuse_palette ? &frame->palette: NULL,
53✔
339
                            &frame->ncolors,
53✔
340
                            reqcolors,
53✔
341
                            &frame->pixelformat,
209✔
342
                            pchunk->allocator);
209✔
343
        if (SIXEL_FAILED(status)) {
209!
344
            goto end;
×
345
        }
346
        sixel_frame_set_pixels(frame, pixels);
209✔
347
    } else if (chunk_is_pnm(pchunk)) {
528✔
348
        status = sixel_frame_new(&frame, pchunk->allocator);
40✔
349
        if (SIXEL_FAILED(status)) {
40!
350
            goto end;
×
351
        }
352
        if (pchunk->size > INT_MAX) {
40!
353
            status = SIXEL_BAD_INTEGER_OVERFLOW;
×
354
            goto end;
×
355
        }
356
        status = load_pnm(pchunk->buffer,
50✔
357
                          (int)pchunk->size,
10✔
358
                          frame->allocator,
10✔
359
                          &pixels,
360
                          &frame->width,
10✔
361
                          &frame->height,
10✔
362
                          fuse_palette ? &frame->palette: NULL,
10!
363
                          &frame->ncolors,
10✔
364
                          &frame->pixelformat);
40!
365
        if (SIXEL_FAILED(status)) {
40!
366
            goto end;
×
367
        }
368
        sixel_frame_set_pixels(frame, pixels);
40✔
369
    } else if (chunk_is_gif(pchunk)) {
445✔
370
        fnp.fn = fn_load;
32✔
371
        if (pchunk->size > INT_MAX) {
32!
372
            status = SIXEL_BAD_INTEGER_OVERFLOW;
×
373
            goto end;
×
374
        }
375
        status = load_gif(pchunk->buffer,
64✔
376
                          (int)pchunk->size,
8✔
377
                          bgcolor,
8✔
378
                          reqcolors,
8✔
379
                          fuse_palette,
8✔
380
                          fstatic,
8✔
381
                          loop_control,
8✔
382
                          fnp.p,
8✔
383
                          context,
8✔
384
                          pchunk->allocator);
32✔
385
        if (SIXEL_FAILED(status)) {
32✔
386
            goto end;
2✔
387
        }
388
        goto end;
30✔
389
    } else {
390
        status = sixel_frame_new(&frame, pchunk->allocator);
403✔
391
        if (SIXEL_FAILED(status)) {
403!
392
            goto end;
×
393
        }
394
        if (pchunk->size > INT_MAX) {
403!
395
            status = SIXEL_BAD_INTEGER_OVERFLOW;
×
396
            goto end;
×
397
        }
398
        stbi_allocator = pchunk->allocator;
403✔
399
        stbi__start_mem(&stb_context,
403✔
400
                        pchunk->buffer,
403✔
401
                        (int)pchunk->size);
13✔
402
        pixels = stbi__load_and_postprocess_8bit(&stb_context,
793✔
403
                                                 &frame->width,
13✔
404
                                                 &frame->height,
403✔
405
                                                 &depth,
406
                                                 3);
407
        if (pixels == NULL) {
403✔
408
            sixel_helper_set_additional_message(stbi_failure_reason());
22✔
409
            status = SIXEL_STBI_ERROR;
22✔
410
            goto end;
22✔
411
        }
412
        sixel_frame_set_pixels(frame, pixels);
381✔
413
        frame->loop_count = 1;
381✔
414
        switch (depth) {
381!
415
        case 1:
373✔
416
        case 3:
417
        case 4:
418
            frame->pixelformat = SIXEL_PIXELFORMAT_RGB888;
381✔
419
            break;
381✔
420
        default:
421
            nwrite = snprintf(message,
×
422
                              sizeof(message),
423
                              "load_with_builtin() failed.\n"
424
                              "reason: unknown pixel-format.(depth: %d)\n",
425
                              depth);
426
            if (nwrite > 0) {
×
427
                sixel_helper_set_additional_message(message);
×
428
            }
429
            status = SIXEL_STBI_ERROR;
×
430
            goto end;
×
431
        }
432
    }
433

434
    status = sixel_frame_strip_alpha(frame, bgcolor);
630✔
435
    if (SIXEL_FAILED(status)) {
630!
436
        goto end;
×
437
    }
438

439
    status = fn_load(frame, context);
630✔
440
    if (SIXEL_FAILED(status)) {
630!
441
        goto end;
×
442
    }
443

444
    status = SIXEL_OK;
71✔
445

446
end:
600✔
447
    sixel_frame_unref(frame);
684✔
448

449
    return status;
768✔
450
}
84✔
451

452
/* emacs Local Variables:      */
453
/* emacs mode: c               */
454
/* emacs tab-width: 4          */
455
/* emacs indent-tabs-mode: nil */
456
/* emacs c-basic-offset: 4     */
457
/* emacs End:                  */
458
/* vim: set expandtab ts=4 sts=4 sw=4 : */
459
/* EOF */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc