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

saitoha / libsixel / 21602244167

02 Feb 2026 06:23PM UTC coverage: 77.475% (-0.01%) from 77.487%
21602244167

push

github

saitoha
build: fix for mingw64-make

23661 of 50520 branches covered (46.83%)

37662 of 48612 relevant lines covered (77.47%)

40022585.38 hits per line

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

75.97
/src/fromsixel.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2025 libsixel developers. See `AUTHORS`.
5
 * Copyright (c) 2014-2016 Hayaki Saito
6
 * Copyright (c) 2014 kmiya@culti
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
9
 * this software and associated documentation files (the "Software"), to deal in
10
 * the Software without restriction, including without limitation the rights to
11
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12
 * the Software, and to permit persons to whom the Software is furnished to do so,
13
 * subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in all
16
 * copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
 * This file is derived from "sixel" original version (2014-3-2)
25
 * http://nanno.dip.jp/softlib/man/rlogin/sixel.tar.gz
26
 *
27
 */
28

29
/*
30
 * Initial developer of this file is kmiya@culti.
31
 *
32
 * He distributes it under very permissive license which permits
33
 * useing, copying, modification, redistribution, and all other
34
 * public activities without any restrictions.
35
 *
36
 * He declares this is compatible with MIT/BSD/GPL.
37
 *
38
 * Hayaki Saito <saitoha@me.com> modified this and re-licensed
39
 * it to the MIT license.
40
 */
41
#if defined(HAVE_CONFIG_H)
42
#include "config.h"
43
#endif
44

45
/* STDC_HEADERS */
46
#include <stdlib.h>
47
#include <stdio.h>
48

49
#if HAVE_CTYPE_H
50
# include <ctype.h>   /* isdigit */
51
#endif  /* HAVE_CTYPE_H */
52
#if HAVE_STRING_H
53
# include <string.h>  /* memcpy */
54
#endif  /* HAVE_STRING_H */
55
#if HAVE_LIMITS_H
56
# include <limits.h>
57
#endif  /* HAVE_LIMITS_H */
58
#if HAVE_INTTYPES_H
59
# include <inttypes.h>
60
#endif  /* HAVE_INTTYPES_H */
61
#if HAVE_ASSERT_H
62
# include <assert.h>
63
#endif  /* HAVE_ASSERT_H */
64

65
#include <sixel.h>
66
#include "output.h"
67
#include "decoder-image.h"
68
#include "decoder-parallel.h"
69
#include "logger.h"
70

71
#define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) +  (b))
72

73
#define PALVAL(n,a,m) (((n) * (a) + ((m) / 2)) / (m))
74

75
#define SIXEL_XRGB(r,g,b) SIXEL_RGB(PALVAL(r, 255, 100), PALVAL(g, 255, 100), PALVAL(b, 255, 100))
76

77
#define DECSIXEL_PARAMS_MAX 16
78
#define SIXEL_PALETTE_MAX_DECODER 65536
79
typedef unsigned char sixel_decoder_index_t;
80

81
static int
82
sixel_clamp_hls_component(double value)
23,903✔
83
{
84
    int channel;
15,290✔
85

86
    channel = (int)(value + 0.5);
23,903✔
87
    if (channel < 0) {
23,903!
88
        channel = 0;
89
    } else if (channel > 100) {
23,903!
90
        channel = 100;
91
    }
92

93
    return channel;
32,516✔
94
}
8,613✔
95

96
static int const sixel_default_color_table[] = {
97
    SIXEL_XRGB(0,  0,  0),   /*  0 Black    */
98
    SIXEL_XRGB(20, 20, 80),  /*  1 Blue     */
99
    SIXEL_XRGB(80, 13, 13),  /*  2 Red      */
100
    SIXEL_XRGB(20, 80, 20),  /*  3 Green    */
101
    SIXEL_XRGB(80, 20, 80),  /*  4 Magenta  */
102
    SIXEL_XRGB(20, 80, 80),  /*  5 Cyan     */
103
    SIXEL_XRGB(80, 80, 20),  /*  6 Yellow   */
104
    SIXEL_XRGB(53, 53, 53),  /*  7 Gray 50% */
105
    SIXEL_XRGB(26, 26, 26),  /*  8 Gray 25% */
106
    SIXEL_XRGB(33, 33, 60),  /*  9 Blue*    */
107
    SIXEL_XRGB(60, 26, 26),  /* 10 Red*     */
108
    SIXEL_XRGB(33, 60, 33),  /* 11 Green*   */
109
    SIXEL_XRGB(60, 33, 60),  /* 12 Magenta* */
110
    SIXEL_XRGB(33, 60, 60),  /* 13 Cyan*    */
111
    SIXEL_XRGB(60, 60, 33),  /* 14 Yellow*  */
112
    SIXEL_XRGB(80, 80, 80),  /* 15 Gray 75% */
113
};
114

115

116
/*
117
 * Store a single pixel in the image buffer. When the decoder is in direct
118
 * color mode the palette index is translated into an RGBA quadruplet with an
119
 * opaque alpha channel. Indexed mode keeps the original palette entry so the
120
 * caller can compose a palette later.
121
 */
122
static void
123
image_buffer_store_pixel(image_buffer_t *image, size_t pos, int color_index)
273,459,500✔
124
{
125
    unsigned char *bytes;
210,853,880✔
126
    int color;
210,853,880✔
127
    int depth;
210,853,880✔
128

129
    depth = image->depth;
273,459,500✔
130
    if (color_index < 0 || color_index >= SIXEL_PALETTE_MAX_DECODER) {
273,459,500!
131
        return;
132
    }
133

134
    if (depth == 1U) {
273,459,500✔
135
        image->pixels.in_bytes[pos] = (unsigned char)color_index;
269,829,468✔
136
    } else if (depth == 2U) {
128,538,576!
137
        image->pixels.in_shorts[pos] = (unsigned short)color_index;
×
138
    } else {  /* rgba */
139
        color = image->palette[color_index];
3,630,032✔
140
        bytes = image->pixels.in_bytes + pos * 4U;
3,630,032✔
141
        bytes[0] = (unsigned char)((color >> 16) & 0xff);
3,630,032✔
142
        bytes[1] = (unsigned char)((color >> 8) & 0xff);
3,630,032✔
143
        bytes[2] = (unsigned char)(color & 0xff);
3,630,032✔
144
        bytes[3] = 255u;
3,630,032✔
145
    }
146
}
126,584,064!
147

148
/*
149
 * Fill a horizontal run starting at (x, y) with the requested palette index.
150
 * Direct color output expands the span into RGBA bytes to keep the alpha
151
 * channel and the color components consistent with single pixel writes.
152
 */
153
static void
154
image_buffer_fill_span(image_buffer_t *image,
1,757,836✔
155
                       int y,
156
                       int x,
157
                       int repeat,
158
                       int color_index)
159
{
160
    size_t pos;
1,355,195✔
161
    int n;
1,355,195✔
162

163
    pos = (size_t)image->width * (size_t)y + (size_t)x;
1,757,836✔
164

165
    if (image->depth == 1U) {
1,757,836✔
166
        memset(image->pixels.in_bytes + pos, color_index, (size_t)repeat);
1,719,213✔
167
    } else {
800,425✔
168
        for (n = 0; n < repeat; ++n) {
282,633✔
169
            image_buffer_store_pixel(image, pos + (size_t)n, color_index);
244,010✔
170
        }
112,620✔
171
    }
172
}
1,757,836✔
173

174
typedef enum parse_state {
175
    PS_GROUND     = 0,
176
    PS_ESC        = 1,  /* ESC */
177
    PS_DCS        = 2,  /* DCS Device Control String Introducer \033P P...P I...I F */
178
    PS_DECSIXEL   = 3,  /* DECSIXEL body part ", $, -, ? ... ~ */
179
    PS_DECGRA     = 4,  /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
180
    PS_DECGRI     = 5,  /* DECGRI Graphics Repeat Introducer ! Pn Ch */
181
    PS_DECGCI     = 6   /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
182
} parse_state_t;
183

184
typedef struct parser_context {
185
    parse_state_t state;
186
    int pos_x;
187
    int pos_y;
188
    int max_x;
189
    int max_y;
190
    int attributed_pan;
191
    int attributed_pad;
192
    int attributed_ph;
193
    int attributed_pv;
194
    int repeat_count;
195
    int color_index;
196
    int bgindex;
197
    int param;
198
    int nparams;
199
    int params[DECSIXEL_PARAMS_MAX];
200
} parser_context_t;
201

202

203
/*
204
 * Primary color hues:
205
 *  blue:    0 degrees
206
 *  red:   120 degrees
207
 *  green: 240 degrees
208
 */
209
static int
210
hls_to_rgb(int hue, int lum, int sat)
12,419✔
211
{
212
    double min, max;
9,548✔
213
    int r, g, b;
9,548✔
214

215
    r = 0;
12,419✔
216
    g = 0;
12,419✔
217
    b = 0;
12,419✔
218

219
    if (sat == 0) {
12,419✔
220
        r = g = b = lum;
6,845✔
221
    }
168✔
222

223
    /* https://wikimedia.org/api/rest_v1/media/math/render/svg/17e876f7e3260ea7fed73f69e19c71eb715dd09d */
224
    max = lum + sat * (1.0 - (lum > 50 ? (2 * (lum / 100.0) - 1.0): - (2 * (lum / 100.0) - 1.0))) / 2.0;
12,419✔
225

226
    /* https://wikimedia.org/api/rest_v1/media/math/render/svg/f6721b57985ad83db3d5b800dc38c9980eedde1d */
227
    min = lum - sat * (1.0 - (lum > 50 ? (2 * (lum / 100.0) - 1.0): - (2 * (lum / 100.0) - 1.0))) / 2.0;
12,419✔
228

229
    /* sixel hue color ring is roteted -120 degree from nowdays general one. */
230
    hue = (hue + 240) % 360;
12,419✔
231

232
    /* https://wikimedia.org/api/rest_v1/media/math/render/svg/937e8abdab308a22ff99de24d645ec9e70f1e384 */
233
    switch (hue / 60) {
12,419!
234
    case 0:  /* 0 <= hue < 60 */
235
        r = sixel_clamp_hls_component(max);
5,957✔
236
        g = sixel_clamp_hls_component(min + (max - min) * (hue / 60.0));
5,957✔
237
        b = sixel_clamp_hls_component(min);
5,957✔
238
        break;
5,957✔
239
    case 1:  /* 60 <= hue < 120 */
1,188✔
240
        r = sixel_clamp_hls_component(
2,178✔
241
                min + (max - min) * ((120 - hue) / 60.0));
2,178✔
242
        g = sixel_clamp_hls_component(max);
2,178✔
243
        b = sixel_clamp_hls_component(min);
2,178✔
244
        break;
2,178✔
245
    case 2:  /* 120 <= hue < 180 */
246
        r = sixel_clamp_hls_component(min);
956✔
247
        g = sixel_clamp_hls_component(max);
956✔
248
        b = sixel_clamp_hls_component(
956✔
249
                min + (max - min) * ((hue - 120) / 60.0));
956✔
250
        break;
956✔
251
    case 3:  /* 180 <= hue < 240 */
252
        r = sixel_clamp_hls_component(min);
1,063✔
253
        g = sixel_clamp_hls_component(
1,063✔
254
                min + (max - min) * ((240 - hue) / 60.0));
1,063✔
255
        b = sixel_clamp_hls_component(max);
1,063✔
256
        break;
1,063✔
257
    case 4:  /* 240 <= hue < 300 */
700✔
258
        r = sixel_clamp_hls_component(
1,312✔
259
                min + (max - min) * ((hue - 240) / 60.0));
1,312✔
260
        g = sixel_clamp_hls_component(min);
1,312✔
261
        b = sixel_clamp_hls_component(max);
1,312✔
262
        break;
1,312✔
263
    case 5:  /* 300 <= hue < 360 */
264
        r = sixel_clamp_hls_component(max);
953✔
265
        g = sixel_clamp_hls_component(min);
953✔
266
        b = sixel_clamp_hls_component(
953✔
267
                min + (max - min) * ((360 - hue) / 60.0));
953✔
268
        break;
953✔
269
    default:
270
#if HAVE___BUILTIN_UNREACHABLE
271
        __builtin_unreachable();
×
272
#endif
273
        break;
6,677✔
274
    }
275

276
    return SIXEL_XRGB(r, g, b);
15,290✔
277
}
2,871✔
278

279

280
SIXELSTATUS
281
image_buffer_init(
2,413✔
282
    image_buffer_t        *image,
283
    int                    width,
284
    int                    height,
285
    int                    bgindex,
286
    int                    depth,
287
    sixel_allocator_t     *allocator)
288
{
289
    SIXELSTATUS status = SIXEL_FALSE;
2,413✔
290
    size_t size;
1,856✔
291
    size_t stride;
1,856✔
292
    int i;
1,856✔
293
    int n;
1,856✔
294
    int r;
1,856✔
295
    int g;
1,856✔
296
    int b;
1,856✔
297

298
    /* check parameters */
299
    if (width <= 0) {
2,413!
300
        sixel_helper_set_additional_message(
×
301
            "image_buffer_init: an invalid width parameter detected.");
302
        status = SIXEL_BAD_INPUT;
×
303
        goto end;
×
304
    }
305
    if (height <= 0) {
2,413!
306
        sixel_helper_set_additional_message(
×
307
            "image_buffer_init: an invalid width parameter detected.");
308
        status = SIXEL_BAD_INPUT;
×
309
        goto end;
×
310
    }
311
    if (width > SIXEL_WIDTH_LIMIT) {
2,413!
312
        sixel_helper_set_additional_message(
×
313
            "image_buffer_init: given width parameter is too huge.");
314
        status = SIXEL_BAD_INPUT;
×
315
        goto end;
×
316
    }
317
    if (height > SIXEL_HEIGHT_LIMIT) {
2,413!
318
        sixel_helper_set_additional_message(
×
319
            "image_buffer_init: given height parameter is too huge.");
320
        status = SIXEL_BAD_INPUT;
×
321
        goto end;
×
322
    }
323

324
    image->depth = depth;
2,413✔
325
    stride = (size_t)width * depth;
2,413✔
326
    size = stride * (size_t)height;
2,413✔
327
    image->width = width;
2,413✔
328
    image->height = height;
2,413✔
329
    image->pixels.p = (unsigned char *)sixel_allocator_malloc(allocator, size);
2,413✔
330
    if (depth == 4U) {
2,413✔
331
        image->ncolors = (-1);
81✔
332
    } else {
46✔
333
        image->ncolors = 2;
2,332✔
334
    }
335

336
    if (image->pixels.p == NULL) {
2,413!
337
        sixel_helper_set_additional_message(
×
338
            "sixel_deocde_raw: sixel_allocator_malloc() failed.");
339
        status = SIXEL_BAD_ALLOCATION;
×
340
        goto end;
×
341
    }
342
    if (depth == 4U) {
2,413✔
343
        memset(image->pixels.p, 0U, size);
81✔
344
    } else {
46✔
345
        if (depth == 1U) {
2,332!
346
            memset(image->pixels.p, bgindex, size);
2,332✔
347
        } else {  /* 2U */
1,077✔
348
            for (n = 0; n <= width * height; ++n) {
×
349
                image_buffer_store_pixel(image, n, bgindex);
×
350
            }
351
        }
352

353
        /* palette initialization */
354
        for (n = 0; n < 16; n++) {
39,644✔
355
            image->palette[n] = sixel_default_color_table[n];
37,312✔
356
        }
17,232✔
357

358
        /* colors 16-231 are a 6x6x6 color cube */
359
        for (r = 0; r < 6; r++) {
16,324✔
360
            for (g = 0; g < 6; g++) {
97,944✔
361
                for (b = 0; b < 6; b++) {
587,664✔
362
                    image->palette[n++] = SIXEL_RGB(r * 51, g * 51, b * 51);
503,712✔
363
                }
232,632✔
364
            }
38,772✔
365
        }
6,462✔
366

367
        /* colors 232-255 are a grayscale ramp, intentionally leaving out */
368
        for (i = 0; i < 24; i++) {
58,300✔
369
            image->palette[n++] = SIXEL_RGB(i * 11, i * 11, i * 11);
55,968✔
370
        }
25,848✔
371

372
#if HAVE_ASSERT
373
        assert(n == 256);
374
#endif  /* HAVE_ASSERT */
375

376
    for (n = 256; n < SIXEL_PALETTE_MAX_DECODER; n++) {
152,235,292✔
377
        image->palette[n] = SIXEL_RGB(255, 255, 255);
152,232,960✔
378
    }
70,306,560✔
379
    }
380
    status = SIXEL_OK;
1,123✔
381

382
end:
1,290✔
383
    return status;
2,979✔
384
}
566✔
385

386

387
SIXELSTATUS
388
image_buffer_resize(
5,217✔
389
    image_buffer_t        *image,
390
    int                    width,
391
    int                    height,
392
    int                    bgindex,
393
    sixel_allocator_t     *allocator)
394
{
395
    SIXELSTATUS status = SIXEL_FALSE;
5,217✔
396
    size_t size;
4,008✔
397
    unsigned char *alt_buffer;
4,008✔
398
    int n;
4,008✔
399
    int min_height;
4,008✔
400
    size_t stride;
4,008✔
401
    size_t old_stride;
4,008✔
402
    size_t copy_stride;
4,008✔
403
    size_t depth = image->depth;
5,217✔
404

405
    /* check parameters */
406
    if (width <= 0) {
5,217!
407
        sixel_helper_set_additional_message(
×
408
            "image_buffer_init: an invalid width parameter detected.");
409
        status = SIXEL_BAD_INPUT;
×
410
        goto end;
×
411
    }
412
    if (height <= 0) {
5,217!
413
        sixel_helper_set_additional_message(
×
414
            "image_buffer_init: an invalid width parameter detected.");
415
        status = SIXEL_BAD_INPUT;
×
416
        goto end;
×
417
    }
418
    if (height > SIXEL_HEIGHT_LIMIT) {
5,217!
419
        sixel_helper_set_additional_message(
×
420
            "image_buffer_init: given height parameter is too huge.");
421
        status = SIXEL_BAD_INPUT;
×
422
        goto end;
×
423
    }
424
    if (width > SIXEL_WIDTH_LIMIT) {
5,217!
425
        sixel_helper_set_additional_message(
×
426
            "image_buffer_init: given width parameter is too huge.");
427
        status = SIXEL_BAD_INPUT;
×
428
        goto end;
×
429
    }
430
    if (height > SIXEL_HEIGHT_LIMIT) {
5,217!
431
        sixel_helper_set_additional_message(
432
            "image_buffer_init: given height parameter is too huge.");
433
        status = SIXEL_BAD_INPUT;
434
        goto end;
435
    }
436

437
    stride = (size_t)width * depth;
5,217✔
438
    size = stride * (size_t)height;
5,217✔
439
    alt_buffer = (sixel_decoder_index_t *)sixel_allocator_malloc(allocator, size);
5,217✔
440
    if (alt_buffer == NULL || size == 0) {
5,217!
441
        /* free source image */
442
        sixel_allocator_free(allocator, image->pixels.p);
×
443
        image->pixels.p = NULL;
×
444
        sixel_helper_set_additional_message(
×
445
            "image_buffer_resize: sixel_allocator_malloc() failed.");
446
        status = SIXEL_BAD_ALLOCATION;
×
447
        goto end;
×
448
    }
449

450
    min_height = height > image->height ? image->height: height;
5,217✔
451
    old_stride = (size_t)image->width * depth;
5,217✔
452
    if (width > image->width) {
5,217✔
453
        copy_stride = old_stride;
1,273✔
454
        for (n = 0; n < min_height; ++n) {
7,387✔
455
            memcpy(alt_buffer + stride * (size_t)n,
4,649✔
456
                   image->pixels.in_bytes + old_stride * (size_t)n,
2,494!
457
                   copy_stride);
458
            if (stride > copy_stride) {
4,649!
459
                if (depth == 4U) {  /* rgba */
4,649✔
460
                    memset(alt_buffer + stride * (size_t)n + copy_stride,
1,017✔
461
                           0,
462
                           stride - copy_stride);
463
                } else {
478✔
464
                    memset(alt_buffer + stride * (size_t)n + copy_stride,
3,632✔
465
                           bgindex,
466
                           stride - copy_stride);
467
                }
468
            }
2,155✔
469
        }
2,155✔
470
    } else {
1,273✔
471
        copy_stride = stride;
1,160✔
472
        for (n = 0; n < min_height; ++n) {
146,271✔
473
            memcpy(alt_buffer + stride * (size_t)n,
143,792✔
474
                   image->pixels.in_bytes + old_stride * (size_t)n,
77,348✔
475
                   copy_stride);
476
        }
66,444✔
477
    }
478

479
    if (height > image->height) {
5,217✔
480
        if (depth == 4u) {  /* rgba */
3,579✔
481
            memset(alt_buffer + stride * (size_t)image->height,
95✔
482
                   0,
483
                   stride * (size_t)(height - image->height));
35✔
484
        } else {
60✔
485
            memset(alt_buffer + stride * (size_t)image->height,
3,484✔
486
                   bgindex,
487
                   stride * (size_t)(height - image->height));
1,876✔
488
        }
489
    }
1,668✔
490

491
    /* free source image */
492
    sixel_allocator_free(allocator, image->pixels.p);
5,217✔
493

494
    image->pixels.in_bytes = alt_buffer;
5,217✔
495
    image->width = width;
5,217✔
496
    image->height = height;
5,217✔
497

498
    status = SIXEL_OK;
5,217✔
499

500
end:
2,784✔
501
    return status;
6,441✔
502
}
1,224✔
503

504

505
static SIXELSTATUS
506
parser_context_init(parser_context_t *context)
2,413✔
507
{
508
    SIXELSTATUS status = SIXEL_FALSE;
2,413✔
509

510
    context->state = PS_GROUND;
2,413✔
511
    context->pos_x = 0;
2,413✔
512
    context->pos_y = 0;
2,413✔
513
    context->max_x = 0;
2,413✔
514
    context->max_y = 0;
2,413✔
515
    context->attributed_pan = 2;
2,413✔
516
    context->attributed_pad = 1;
2,413✔
517
    context->attributed_ph = 0;
2,413✔
518
    context->attributed_pv = 0;
2,413✔
519
    context->repeat_count = 1;
2,413✔
520
    context->color_index = 15;
2,413✔
521
    context->bgindex = (-1);
2,413✔
522
    context->nparams = 0;
2,413✔
523
    context->param = 0;
2,413✔
524

525
    status = SIXEL_OK;
2,413✔
526

527
    return status;
2,979✔
528
}
566✔
529

530

531
static SIXELSTATUS
532
safe_addition_for_params(parser_context_t *context, unsigned char *p)
90,806,491✔
533
{
534
    SIXELSTATUS status = SIXEL_FALSE;
90,806,491✔
535
    int x;
70,037,556✔
536

537
    x = *p - '0'; /* 0 <= x <= 9 */
90,806,491✔
538
    if ((context->param > INT_MAX / 10) || (x > INT_MAX - context->param * 10)) {
90,806,491!
539
        status = SIXEL_BAD_INTEGER_OVERFLOW;
×
540
        sixel_helper_set_additional_message(
×
541
            "safe_addition_for_params: ingeger overflow detected.");
542
        goto end;
×
543
    }
544
    context->param = context->param * 10 + x;
90,806,491✔
545
    status = SIXEL_OK;
90,806,491✔
546

547
end:
48,799,700✔
548
    return status;
112,044,347✔
549
}
21,237,856✔
550

551

552
/* convert sixel data into indexed pixel bytes and palette data */
553
SIXELAPI SIXELSTATUS
554
sixel_decode_raw_impl(
2,413✔
555
    unsigned char     *p,         /* sixel bytes */
556
    int                len,       /* size of sixel bytes */
557
    image_buffer_t    *image,
558
    parser_context_t  *context,
559
    sixel_allocator_t *allocator, /* allocator object */
560
    sixel_logger_t    *logger,
561
    int logger_prepared)
562
{
563
    SIXELSTATUS status = SIXEL_FALSE;
2,413✔
564
    int n;
1,856✔
565
    int i;
1,856✔
566
    int y;
1,856✔
567
    int bits;
1,856✔
568
    int sixel_vertical_mask;
1,856✔
569
    int sx;
1,856✔
570
    int sy;
1,856✔
571
    int c;
1,856✔
572
    size_t pos;
1,856✔
573
    unsigned char *p0 = p;
2,413✔
574
#if SIXEL_ENABLE_THREADS
575
    int parallel_started = 0;
2,043✔
576
    int raster_ready = 0;
2,043✔
577
    int palette_ready = 0;
2,043✔
578
    unsigned char *parallel_anchor = p;
2,043✔
579
#else
580
    (void) logger;
370✔
581
    (void) logger_prepared;
370✔
582
#endif  /* SIXEL_ENABLE_THREADS */
583

584
    while (p < p0 + len) {
400,774,295✔
585
        switch (context->state) {
400,772,977!
586
        case PS_GROUND:
1,290✔
587
            switch (*p) {
2,413!
588
            case 0x1b:
1,276✔
589
                context->state = PS_ESC;
2,387✔
590
                p++;
2,387✔
591
                break;
2,387✔
592
            case 0x90:
14✔
593
                context->state = PS_DCS;
26✔
594
                p++;
26✔
595
                break;
26✔
596
            case 0x9c:
597
                p++;
×
598
                goto finalize;
×
599
            default:
600
                p++;
×
601
                break;
×
602
            }
603
            break;
1,123✔
604

605
        case PS_ESC:
2,552✔
606
            switch (*p) {
4,772!
607
            case '\\':
1,276✔
608
            case 0x9c:
609
                p++;
2,385✔
610
                goto finalize;
2,385✔
611
            case 'P':
1,276✔
612
                context->param = -1;
2,387✔
613
                context->state = PS_DCS;
2,387✔
614
                p++;
2,387✔
615
                break;
2,387✔
616
            default:
617
                p++;
×
618
                break;
×
619
            }
620
            break;
1,111✔
621

622
        case PS_DCS:
1,346✔
623
            switch (*p) {
2,517!
624
            case 0x1b:
625
                context->state = PS_ESC;
×
626
                p++;
×
627
                break;
×
628
            case '0':
629
            case '1':
630
            case '2':
631
            case '3':
632
            case '4':
633
            case '5':
634
            case '6':
635
            case '7':
636
            case '8':
637
            case '9':
638
                if (context->param < 0) {
×
639
                    context->param = 0;
×
640
                }
641
                status = safe_addition_for_params(context, p);
×
642
                if (SIXEL_FAILED(status)) {
×
643
                    goto end;
×
644
                }
645
                p++;
×
646
                break;
×
647
            case ';':
648
                if (context->param < 0) {
×
649
                    context->param = 0;
×
650
                }
651
                if (context->nparams < DECSIXEL_PARAMS_MAX) {
×
652
                    context->params[context->nparams++] = context->param;
×
653
                }
654
                context->param = 0;
×
655
                p++;
×
656
                break;
×
657
            case 'q':
1,290✔
658
                if (context->param >= 0 && context->nparams < DECSIXEL_PARAMS_MAX) {
2,413!
659
                    context->params[context->nparams++] = context->param;
26✔
660
                }
12✔
661
                if (context->nparams > 0) {
2,413✔
662
                    /* Pn1 */
663
                    switch (context->params[0]) {
26!
664
                    case 0:
14✔
665
                    case 1:
666
                        context->attributed_pad = 2;
26✔
667
                        break;
26✔
668
                    case 2:
669
                        context->attributed_pad = 5;
×
670
                        break;
×
671
                    case 3:
672
                    case 4:
673
                        context->attributed_pad = 4;
×
674
                        break;
×
675
                    case 5:
676
                    case 6:
677
                        context->attributed_pad = 3;
×
678
                        break;
×
679
                    case 7:
680
                    case 8:
681
                        context->attributed_pad = 2;
×
682
                        break;
×
683
                    case 9:
684
                        context->attributed_pad = 1;
×
685
                        break;
×
686
                    default:
687
                        context->attributed_pad = 2;
×
688
                        break;
×
689
                    }
690
                }
12✔
691

692
                if (context->nparams > 2) {
2,413!
693
                    /* Pn3 */
694
                    if (context->params[2] == 0) {
×
695
                        context->params[2] = 10;
×
696
                    }
697
                    context->attributed_pan = context->attributed_pan * context->params[2] / 10;
×
698
                    context->attributed_pad = context->attributed_pad * context->params[2] / 10;
×
699
                    if (context->attributed_pan <= 0) {
×
700
                        context->attributed_pan = 1;
×
701
                    }
702
                    if (context->attributed_pad <= 0) {
×
703
                        context->attributed_pad = 1;
×
704
                    }
705
                }
706
                context->nparams = 0;
2,413✔
707
                context->state = PS_DECSIXEL;
2,413✔
708
                p++;
2,413✔
709
                break;
2,413✔
710
            default:
56✔
711
                p++;
104✔
712
                break;
104✔
713
            }
714
            break;
1,171✔
715

716
        case PS_DECSIXEL:
142,962,973✔
717
            switch (*p) {
266,072,553✔
718
            case '\x1b':
1,276✔
719
                context->state = PS_ESC;
2,385✔
720
                p++;
2,385✔
721
                break;
2,385✔
722
            case '"':
1,276✔
723
                context->param = 0;
2,387✔
724
                context->nparams = 0;
2,387✔
725
                context->state = PS_DECGRA;
2,387✔
726
                p++;
2,387✔
727
                break;
2,387✔
728
            case '!':
8,496,581✔
729
                context->param = 0;
15,782,786✔
730
                context->nparams = 0;
15,782,786✔
731
                context->state = PS_DECGRI;
15,782,786✔
732
                p++;
15,782,786✔
733
                break;
15,782,786✔
734
            case '#':
13,991,731✔
735
                context->param = 0;
26,056,510✔
736
                context->nparams = 0;
26,056,510✔
737
#if SIXEL_ENABLE_THREADS
738
                if (!palette_ready) {
22,034,938✔
739
                    palette_ready = 1;
9,971,240✔
740
                }
1,081✔
741
#endif  /* SIXEL_ENABLE_THREADS */
742
                context->state = PS_DECGCI;
26,056,510✔
743
                p++;
26,056,510✔
744
                break;
26,056,510✔
745
            case '$':
581,859✔
746
                /* DECGCR Graphics Carriage Return */
747
                context->pos_x = 0;
1,083,844✔
748
#if SIXEL_ENABLE_THREADS
749
                if (!palette_ready) {
917,005!
750
                    palette_ready = 1;
415,020✔
751
                }
752
                if (!parallel_started && raster_ready && palette_ready) {
917,005!
753
                    status = sixel_decoder_parallel_request_start(
×
754
                        image->depth == 4U ? 1: 0,
755
                        p0,
756
                        len,
757
                        parallel_anchor,
758
                        image,
759
                        image->palette,
760
                        logger_prepared ? logger : NULL);
×
761
                    parallel_started = 1;
762
                    if (status == SIXEL_FALSE) {
×
763
                        /* Parallel decode aborted; continue serially. */
764
                    } else {
765
                        goto end;
766
                    }
767
                }
768
#endif  /* SIXEL_ENABLE_THREADS */
769
                p++;
1,083,844✔
770
                break;
1,083,844✔
771
            case '-':
47,309✔
772
                /* DECGNL Graphics Next Line */
773
                context->pos_x = 0;
88,114✔
774
                context->pos_y += 6;
88,114✔
775
#if SIXEL_ENABLE_THREADS
776
                if (!palette_ready) {
74,556!
777
                    palette_ready = 1;
33,751✔
778
                }
779
                if (!parallel_started && raster_ready && palette_ready) {
74,556!
780
                    status = sixel_decoder_parallel_request_start(
×
781
                        image->depth == 4U ? 1: 0,
782
                        p0,
783
                        len,
784
                        parallel_anchor,
785
                        image,
786
                        image->palette,
787
                        logger_prepared ? logger : NULL);
×
788
                    parallel_started = 1;
789
                    if (status == SIXEL_FALSE) {
×
790
                        /* Parallel decode aborted; continue serially. */
791
                    } else {
792
                        goto end;
793
                    }
794
                }
795
#endif  /* SIXEL_ENABLE_THREADS */
796
                p++;
88,114✔
797
                break;
88,114✔
798
            default:
119,842,941✔
799
                if (*p >= '?' && *p <= '~') {  /* sixel characters */
223,056,527!
800
#if SIXEL_ENABLE_THREADS
801
                    if (!palette_ready) {
188,493,123✔
802
                        palette_ready = 1;
85,367,545✔
803
                    }
42✔
804
                    if (!parallel_started && raster_ready && palette_ready) {
188,493,123!
805
                        status = sixel_decoder_parallel_request_start(
2,021!
806
                            image->depth == 4U ? 1: 0,
2,021✔
807
                            p0,
1,111✔
808
                            len,
1,111✔
809
                            parallel_anchor,
1,111✔
810
                            image,
1,111✔
811
                            image->palette,
2,021✔
812
                            logger_prepared ? logger : NULL);
1,111!
813
                        parallel_started = 1;
2,021✔
814
                        if (status == SIXEL_FALSE) {
2,021!
815
                            /* Parallel decode aborted; continue serially. */
816
                        } else {
1,111✔
817
                            goto end;
818
                        }
819
                    }
1,111✔
820
#endif  /* SIXEL_ENABLE_THREADS */
821

822
                    sx = image->width;
222,865,652✔
823
                    while (sx < context->pos_x + context->repeat_count) {
222,866,198✔
824
                        sx *= 2;
546✔
825
                    }
826

827
                    sy = image->height;
222,865,652✔
828
                    while (sy < context->pos_y + 6) {
222,867,136✔
829
                        sy *= 2;
1,484✔
830
                    }
831

832
                    if (sx > image->width || sy > image->height) {
222,865,652✔
833
                        status = image_buffer_resize(image, sx, sy, context->bgindex, allocator);
1,623✔
834
                        if (SIXEL_FAILED(status)) {
1,623!
835
                            goto end;
×
836
                        }
837
                    }
757✔
838

839
                    if (image->depth != 4U &&
222,865,652✔
840
                            context->color_index > image->ncolors) {
220,481,096✔
841
                        image->ncolors = context->color_index;
91✔
842
                    }
42✔
843

844
                    if (context->pos_x < 0 || context->pos_y < 0) {
222,865,652!
845
                        status = SIXEL_BAD_INPUT;
×
846
                        goto end;
×
847
                    }
848
                    bits = *p - '?';
222,865,652✔
849

850
                    if (bits == 0) {
222,865,652✔
851
                        context->pos_x += context->repeat_count;
66,016,358✔
852
                    } else {
30,537,627✔
853
                        sixel_vertical_mask = 0x01;
156,849,294✔
854
                        if (context->repeat_count <= 1) {
156,849,294✔
855
                            for (i = 0; i < 6; i++) {
1,092,869,393✔
856
                                if ((bits & sixel_vertical_mask) != 0) {
936,745,194✔
857
                                    pos =
273,215,490✔
858
                                        (size_t)image->width *
399,686,934✔
859
                                        (size_t)(context->pos_y + i) +
399,686,934✔
860
                                        (size_t)context->pos_x;
273,215,490✔
861
                                    image_buffer_store_pixel(
273,215,490✔
862
                                        image,
126,471,444✔
863
                                        pos,
126,471,444✔
864
                                        context->color_index);
126,471,444✔
865
                                    if (context->max_x < context->pos_x) {
273,215,490✔
866
                                        context->max_x = context->pos_x;
434,550✔
867
                                    }
201,571✔
868
                                    if (context->max_y < (context->pos_y + i)) {
273,215,490✔
869
                                        context->max_y = context->pos_y + i;
322,444✔
870
                                    }
150,403✔
871
                                }
126,471,444✔
872
                                sixel_vertical_mask <<= 1;
936,745,194✔
873
                            }
433,511,352✔
874
                            context->pos_x += 1;
156,124,199✔
875
                        } else {
72,251,892✔
876
                            /* context->repeat_count > 1 */
877
                            for (i = 0; i < 6; i++) {
4,056,822✔
878
                                if ((bits & sixel_vertical_mask) != 0) {
3,331,727✔
879
                                    c = sixel_vertical_mask << 1;
738,993✔
880
                                    for (n = 1; (i + n) < 6; n++) {
1,757,836✔
881
                                        if ((bits & c) == 0) {
1,426,493✔
882
                                            break;
188,228✔
883
                                        }
884
                                        c <<= 1;
1,018,843✔
885
                                    }
475,787✔
886
                                    for (y = context->pos_y + i; y < context->pos_y + i + n; ++y) {
2,496,829✔
887
                                        image_buffer_fill_span(
1,757,836✔
888
                                            image,
818,251✔
889
                                            y,
818,251✔
890
                                            context->pos_x,
818,251✔
891
                                            context->repeat_count,
818,251✔
892
                                            context->color_index);
818,251✔
893
                                    }
818,251✔
894
                                    if (context->max_x < (context->pos_x + context->repeat_count - 1)) {
738,993✔
895
                                        context->max_x = context->pos_x + context->repeat_count - 1;
9,765✔
896
                                    }
4,428✔
897
                                    if (context->max_y < (context->pos_y + i + n - 1)) {
738,993✔
898
                                        context->max_y = context->pos_y + i + n - 1;
3,271✔
899
                                    }
1,571✔
900
                                    i += (n - 1);
738,993✔
901
                                    sixel_vertical_mask <<= (n - 1);
738,993✔
902
                                }
342,464✔
903
                                sixel_vertical_mask <<= 1;
3,331,727✔
904
                            }
1,540,819✔
905
                            context->pos_x += context->repeat_count;
725,095✔
906
                        }
907
                    }
908
                    context->repeat_count = 1;
222,865,652✔
909
                }
103,125,620✔
910
                p++;
223,056,527✔
911
                break;
223,056,527✔
912
            }
913
            break;
123,109,580✔
914

915
        case PS_DECGRA:
13,773✔
916
            /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
917
            switch (*p) {
25,733!
918
            case '\x1b':
919
                context->state = PS_ESC;
×
920
                p++;
×
921
                break;
×
922
            case '0':
8,669✔
923
            case '1':
924
            case '2':
925
            case '3':
926
            case '4':
927
            case '5':
928
            case '6':
929
            case '7':
930
            case '8':
931
            case '9':
932
                status = safe_addition_for_params(context, p);
16,185✔
933
                if (SIXEL_FAILED(status)) {
16,185!
934
                    goto end;
×
935
                }
936
                p++;
16,185✔
937
                break;
16,185✔
938
            case ';':
3,828✔
939
                if (context->nparams < DECSIXEL_PARAMS_MAX) {
7,161!
940
                    context->params[context->nparams++] = context->param;
7,161✔
941
                }
3,333✔
942
                context->param = 0;
7,161✔
943
                p++;
7,161✔
944
                break;
7,161✔
945
            default:
1,276✔
946
                if (context->nparams < DECSIXEL_PARAMS_MAX) {
2,387!
947
                    context->params[context->nparams++] = context->param;
2,387✔
948
                }
1,111✔
949
                if (context->nparams > 0) {
2,387!
950
                    context->attributed_pad = context->params[0];
2,387✔
951
                }
1,111✔
952
                if (context->nparams > 1) {
2,387!
953
                    context->attributed_pan = context->params[1];
2,387✔
954
                }
1,111✔
955
                if (context->nparams > 2 && context->params[2] > 0) {
2,387!
956
                    context->attributed_ph = context->params[2];
2,387✔
957
                }
1,111✔
958
                if (context->nparams > 3 && context->params[3] > 0) {
2,387!
959
                    context->attributed_pv = context->params[3];
2,387✔
960
                }
1,111✔
961

962
                if (context->attributed_pan <= 0) {
2,387!
963
                    context->attributed_pan = 1;
×
964
                }
965
                if (context->attributed_pad <= 0) {
2,387!
966
                    context->attributed_pad = 1;
×
967
                }
968

969
                if (image->width < context->attributed_ph ||
2,387!
970
                        image->height < context->attributed_pv) {
65!
971
                    sx = context->attributed_ph;
2,322✔
972
                    if (image->width > context->attributed_ph) {
2,322!
973
                        sx = image->width;
974
                    }
975

976
                    sy = context->attributed_pv;
2,322✔
977
                    if (image->height > context->attributed_pv) {
2,322!
978
                        sy = image->height;
979
                    }
980

981
                    status = image_buffer_resize(image, sx, sy, context->bgindex, allocator);
2,322✔
982
                    if (SIXEL_FAILED(status)) {
2,322!
983
                        goto end;
×
984
                    }
985
                }
1,081✔
986
#if SIXEL_ENABLE_THREADS
987
                if (!raster_ready && context->attributed_ph > 0 &&
2,021!
988
                        context->attributed_pv > 0) {
2,021!
989
                    raster_ready = 1;
2,021✔
990
                }
1,111✔
991
                parallel_anchor = p;
2,021✔
992
#endif  /* SIXEL_ENABLE_THREADS */
993
                context->state = PS_DECSIXEL;
2,387✔
994
                context->param = 0;
2,387✔
995
                context->nparams = 0;
2,387✔
996
            }
1,111✔
997
            break;
11,960✔
998

999
        case PS_DECGRI:
19,835,583✔
1000
            /* DECGRI Graphics Repeat Introducer ! Pn Ch */
1001
            switch (*p) {
36,853,143!
1002
            case '\x1b':
1003
                context->state = PS_ESC;
×
1004
                p++;
×
1005
                break;
×
1006
            case '0':
11,339,002✔
1007
            case '1':
1008
            case '2':
1009
            case '3':
1010
            case '4':
1011
            case '5':
1012
            case '6':
1013
            case '7':
1014
            case '8':
1015
            case '9':
1016
                status = safe_addition_for_params(context, p);
21,070,357✔
1017
                if (SIXEL_FAILED(status)) {
21,070,357!
1018
                    goto end;
×
1019
                }
1020
                p++;
21,070,357✔
1021
                break;
21,070,357✔
1022
            default:
8,496,581✔
1023
                context->repeat_count = context->param;
15,782,786✔
1024
                if (context->repeat_count == 0) {
15,782,786!
1025
                    context->repeat_count = 1;
×
1026
                }
1027
                if (context->repeat_count > 0xffff) {  /* check too huge number */
15,782,786!
1028
                    status = SIXEL_BAD_INPUT;
×
1029
                    sixel_helper_set_additional_message(
×
1030
                        "sixel_decode_raw_impl: detected too huge repeat parameter.");
1031
                    goto end;
×
1032
                }
1033
                context->state = PS_DECSIXEL;
15,782,786✔
1034
                context->param = 0;
15,782,786✔
1035
                context->nparams = 0;
15,782,786✔
1036
                break;
15,782,786✔
1037
            }
1038
            break;
17,017,560✔
1039

1040
        case PS_DECGCI:
52,538,033✔
1041
            /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
1042
            switch (*p) {
97,811,846!
1043
            case '\x1b':
1044
                context->state = PS_ESC;
×
1045
                p++;
×
1046
                break;
×
1047
            case '0':
37,452,029✔
1048
            case '1':
1049
            case '2':
1050
            case '3':
1051
            case '4':
1052
            case '5':
1053
            case '6':
1054
            case '7':
1055
            case '8':
1056
            case '9':
1057
                status = safe_addition_for_params(context, p);
69,719,949✔
1058
                if (SIXEL_FAILED(status)) {
69,719,949!
1059
                    goto end;
×
1060
                }
1061
                p++;
69,719,949✔
1062
                break;
69,719,949✔
1063
            case ';':
1,094,273✔
1064
                if (context->nparams < DECSIXEL_PARAMS_MAX) {
2,035,387!
1065
                    context->params[context->nparams++] = context->param;
2,035,387✔
1066
                }
941,114✔
1067
                context->param = 0;
2,035,387✔
1068
                p++;
2,035,387✔
1069
                break;
2,035,387✔
1070
            default:
13,991,731✔
1071
                context->state = PS_DECSIXEL;
26,056,510✔
1072
                if (context->nparams < DECSIXEL_PARAMS_MAX) {
26,056,510!
1073
                    context->params[context->nparams++] = context->param;
26,056,510✔
1074
                }
12,064,779✔
1075
                context->param = 0;
26,056,510✔
1076

1077
                if (context->nparams > 0) {
26,056,510!
1078
                    context->color_index = context->params[0];
26,056,510✔
1079
                    if (context->color_index < 0) {
26,056,510!
1080
                        context->color_index = 0;
×
1081
                    } else if (context->color_index >= SIXEL_PALETTE_MAX_DECODER) {
26,056,510!
1082
                        context->color_index = SIXEL_PALETTE_MAX_DECODER - 1;
×
1083
                    }
1084
                }
12,064,779✔
1085

1086
                if (context->color_index + 1 > image->ncolors) {
26,056,510✔
1087
                    image->ncolors = context->color_index + 1;
504,342✔
1088
                    if (image->ncolors > SIXEL_PALETTE_MAX_DECODER) {
504,342!
1089
                        image->ncolors = SIXEL_PALETTE_MAX_DECODER;
×
1090
                    }
1091
                }
233,198✔
1092

1093
                if (context->nparams > 4) {
26,056,510✔
1094
                    if (context->params[1] == 1) {
508,824✔
1095
                        /* HLS */
1096
                        if (context->params[2] > 360) {
12,419!
1097
                            context->params[2] = 360;
×
1098
                        }
1099
                        if (context->params[3] > 100) {
12,419!
1100
                            context->params[3] = 100;
×
1101
                        }
1102
                        if (context->params[4] > 100) {
12,419!
1103
                            context->params[4] = 100;
×
1104
                        }
1105
                        image->palette[context->color_index]
12,419✔
1106
                            = hls_to_rgb(context->params[2], context->params[3], context->params[4]);
18,161✔
1107
                    } else if (context->params[1] == 2) {
502,147!
1108
                        /* RGB */
1109
                        if (context->params[2] > 100) {
496,405!
1110
                            context->params[2] = 100;
×
1111
                        }
1112
                        if (context->params[3] > 100) {
496,405!
1113
                            context->params[3] = 100;
×
1114
                        }
1115
                        if (context->params[4] > 100) {
496,405!
1116
                            context->params[4] = 100;
×
1117
                        }
1118
                        image->palette[context->color_index]
496,405✔
1119
                            = SIXEL_XRGB(context->params[2], context->params[3], context->params[4]);
725,931✔
1120
                    }
229,526✔
1121
#if SIXEL_ENABLE_THREADS
1122
                    parallel_anchor = p;
153,740,403✔
1123
#endif  /* SIXEL_ENABLE_THREADS */
1124
                }
235,268✔
1125
                break;
12,064,779✔
1126
            }
1127
            break;
45,273,813✔
1128
        default:
1129
            break;
1130
        }
1131
    }
1132

1133
finalize:
14✔
1134
    if (++context->max_x < context->attributed_ph) {
2,413✔
1135
        context->max_x = context->attributed_ph;
15✔
1136
    }
8✔
1137

1138
    if (++context->max_y < context->attributed_pv) {
2,413✔
1139
        context->max_y = context->attributed_pv;
13✔
1140
    }
6✔
1141

1142
    if (image->width > context->max_x || image->height > context->max_y) {
2,413✔
1143
        status = image_buffer_resize(image, context->max_x, context->max_y, context->bgindex, allocator);
1,272✔
1144
        if (SIXEL_FAILED(status)) {
1,272!
1145
            goto end;
×
1146
        }
1147
    }
595✔
1148

1149
    status = SIXEL_OK;
1,123✔
1150

1151
end:
1,290✔
1152
    return status;
2,979✔
1153
}
566✔
1154

1155

1156
static SIXELSTATUS
1157
sixel_decode_image(
2,413✔
1158
    unsigned char     *p,
1159
    int                len,
1160
    int                initial_width,
1161
    int                initial_height,
1162
    int                depth,
1163
    image_buffer_t    *image,
1164
    parser_context_t  *context,
1165
    sixel_allocator_t *allocator)
1166
{
1167
    SIXELSTATUS status = SIXEL_FALSE;
2,413✔
1168
    sixel_logger_t logger;
1,856✔
1169
    int logger_prepared;
1,856✔
1170

1171
    image->pixels.p = NULL;
2,413✔
1172

1173
    sixel_logger_init(&logger);
2,413✔
1174
    logger_prepared = 0;
2,413✔
1175
    (void)sixel_logger_prepare_env(&logger);
2,413✔
1176
    logger_prepared = logger.active;
2,413✔
1177
    if (logger_prepared) {
2,413!
1178
        /*
1179
         * File I/O window for timeline visualization. The buffer is already
1180
         * populated, but logging the bounds keeps decode timing aligned with
1181
         * encoder logs.
1182
         */
1183
        sixel_logger_logf(&logger,
×
1184
                          "decoder",
1185
                          "io",
1186
                          "start",
1187
                          0,
1188
                          0,
1189
                          0,
1190
                          len,
1191
                          0,
1192
                          len,
1193
                          "reading sixel payload");
1194
    }
1195

1196
    status = parser_context_init(context);
2,413✔
1197
    if (SIXEL_FAILED(status)) {
2,413!
1198
        goto end;
1199
    }
1200

1201
    /*
1202
     * The serial parser always runs first. When palette and raster
1203
     * attributes become available, the parser may request a parallel worker
1204
     * via sixel_decoder_parallel_request_start(). This guarantees bounds
1205
     * checks and logging are consistent before any background decode starts.
1206
     */
1207

1208
    if (logger_prepared) {
2,413!
1209
        /* Mark when the serial parser begins scanning tokens. */
1210
        sixel_logger_logf(&logger,
×
1211
                          "decoder",
1212
                          "controller",
1213
                          "start",
1214
                          0,
1215
                          0,
1216
                          0,
1217
                          len,
1218
                          0,
1219
                          len,
1220
                          "serial parser begin depth=%u",
1221
                          depth);
1222
    }
1223

1224
    status = image_buffer_init(image,
3,536✔
1225
                               initial_width,
1,123✔
1226
                               initial_height,
1,123✔
1227
                               context->bgindex,
1,123✔
1228
                               depth,
1,123✔
1229
                               allocator);
1,123✔
1230
    if (SIXEL_FAILED(status)) {
2,413!
1231
        goto end;
×
1232
    }
1233

1234
    status = sixel_decode_raw_impl(p,
3,536✔
1235
                                   len,
1,123✔
1236
                                   image,
1,123✔
1237
                                   context,
1,123✔
1238
                                   allocator,
1,123✔
1239
                                   &logger,
1240
                                   logger_prepared);
1,123✔
1241
    if (SIXEL_FAILED(status)) {
2,413!
1242
        sixel_allocator_free(allocator, image->pixels.p);
×
1243
        image->pixels.p = NULL;
×
1244
        goto end;
×
1245
    }
1246

1247
    status = SIXEL_OK;
1,123✔
1248

1249
end:
1,290✔
1250
    if (logger_prepared) {
2,413!
1251
        sixel_logger_logf(&logger,
×
1252
                          "decoder",
1253
                          "parser",
1254
                          "finish",
1255
                          0,
1256
                          0,
1257
                          0,
1258
                          len,
1259
                          0,
1260
                          len,
1261
                          "parser status=%d",
1262
                          status);
1263
        sixel_logger_logf(&logger,
×
1264
                          "decoder",
1265
                          "io",
1266
                          "finish",
1267
                          0,
1268
                          0,
1269
                          0,
1270
                          len,
1271
                          0,
1272
                          len,
1273
                          "input processed status=%d",
1274
                          status);
1275
        sixel_logger_close(&logger);
×
1276
    }
1277
    return status;
2,979✔
1278
}
566✔
1279

1280

1281
/* convert sixel data into indexed pixel bytes and palette data */
1282
SIXELAPI SIXELSTATUS
1283
sixel_decode_raw(
2,332✔
1284
    unsigned char       /* in */  *p,           /* sixel bytes */
1285
    int                 /* in */  len,          /* size of sixel bytes */
1286
    unsigned char       /* out */ **pixels,     /* decoded pixels */
1287
    int                 /* out */ *pwidth,      /* image width */
1288
    int                 /* out */ *pheight,     /* image height */
1289
    unsigned char       /* out */ **palette,    /* RGB palette */
1290
    int                 /* out */ *ncolors,     /* palette size (<= 256) */
1291
    sixel_allocator_t   /* in */  *allocator)   /* allocator object or null */
1292
{
1293
    SIXELSTATUS status = SIXEL_FALSE;
2,332✔
1294
    parser_context_t context;
1,798✔
1295
    image_buffer_t image;
1,798✔
1296
    int n;
1,798✔
1297
    int alloc_size;
1,798✔
1298

1299
    image.pixels.p = NULL;
2,332✔
1300

1301
    if (allocator) {
2,332!
1302
        sixel_allocator_ref(allocator);
2,332✔
1303
    } else {
1,077✔
1304
        status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
×
1305
        if (SIXEL_FAILED(status)) {
×
1306
            allocator = NULL;
×
1307
            goto error;
×
1308
        }
1309
    }
1310

1311
    status = sixel_decode_image(p,
3,409✔
1312
                                len,
1,077✔
1313
                                1,  /* initial_width */
1314
                                1,  /* initial_height */
1315
                                1,  /* depth */
1316
                                &image,
1317
                                &context,
1318
                                allocator);
1,077✔
1319
    if (SIXEL_FAILED(status)) {
2,332!
1320
        goto error;
×
1321
    }
1322

1323
    *ncolors = alloc_size = image.ncolors;
2,332✔
1324
    if (alloc_size < SIXEL_PALETTE_MAX_DECODER) {
2,332!
1325
        /* memory access range should be 0 <= 255 */
1326
        alloc_size = SIXEL_PALETTE_MAX_DECODER;
1,077✔
1327
    }
1,077✔
1328
    *palette = (unsigned char *)sixel_allocator_malloc(
3,587✔
1329
        allocator,
1,077✔
1330
        (size_t)(alloc_size * 3));
2,332✔
1331
    if (palette == NULL) {
2,332!
1332
        sixel_allocator_free(allocator, image.pixels.p);
1333
        sixel_helper_set_additional_message(
1334
            "sixel_deocde_raw: sixel_allocator_malloc() failed.");
1335
        status = SIXEL_BAD_ALLOCATION;
1336
        goto error;
1337
    }
1338
    /*
1339
     * Copy the full palette table so default entries remain initialized.
1340
     * This keeps unused slots valid when the image uses fewer colors.
1341
     */
1342
    for (n = 0; n < alloc_size; ++n) {
152,832,284✔
1343
        (*palette)[n * 3 + 0] = image.palette[n] >> 16 & 0xff;
152,829,952✔
1344
        (*palette)[n * 3 + 1] = image.palette[n] >> 8 & 0xff;
152,829,952✔
1345
        (*palette)[n * 3 + 2] = image.palette[n] & 0xff;
152,829,952✔
1346
    }
70,582,272✔
1347

1348
    *pwidth = image.width;
2,332✔
1349
    *pheight = image.height;
2,332✔
1350
    *pixels = image.pixels.p;
2,332✔
1351

1352
    status = SIXEL_OK;
2,332✔
1353
    goto end;
2,332✔
1354

1355
error:
1356
    if (image.pixels.p != NULL) {
×
1357
        if (allocator != NULL) {
×
1358
            sixel_allocator_free(allocator, image.pixels.p);
×
1359
        } else {
1360
            free(image.pixels.p);
×
1361
        }
1362
        image.pixels.p = NULL;
×
1363
    }
1364

1365
end:
1366
    sixel_allocator_unref(allocator);
2,332✔
1367
    return status;
2,875✔
1368
}
543✔
1369

1370

1371
/* convert sixel data into wide-indexed(16bit) pixels and palette data */
1372
SIXELAPI SIXELSTATUS
1373
sixel_decode_wide(
×
1374
    unsigned char       /* in */  *p,           /* sixel bytes */
1375
    int                 /* in */  len,          /* size of sixel bytes */
1376
    unsigned short      /* out */ **pixels,     /* decoded wide indexes */
1377
    int                 /* out */ *pwidth,      /* image width */
1378
    int                 /* out */ *pheight,     /* image height */
1379
    unsigned char       /* out */ **palette,    /* RGB palette */
1380
    int                 /* out */ *ncolors,     /* palette size (<= 256) */
1381
    sixel_allocator_t   /* in */  *allocator)   /* allocator object or null */
1382
{
1383
    SIXELSTATUS status = SIXEL_FALSE;
×
1384
    parser_context_t context;
1385
    image_buffer_t image;
1386
    int n;
1387
    int alloc_size;
1388

1389
    image.pixels.p = NULL;
×
1390

1391
    if (allocator) {
×
1392
        sixel_allocator_ref(allocator);
×
1393
    } else {
1394
        status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
×
1395
        if (SIXEL_FAILED(status)) {
×
1396
            allocator = NULL;
×
1397
            goto error;
×
1398
        }
1399
    }
1400

1401
    status = sixel_decode_image(p,
×
1402
                                len,
1403
                                1,  /* initial width */
1404
                                1,  /* initial height */
1405
                                2,  /* depth */
1406
                                &image,
1407
                                &context,
1408
                                allocator);
1409
    if (SIXEL_FAILED(status)) {
×
1410
        goto error;
×
1411
    }
1412

1413
    *ncolors = alloc_size = image.ncolors;
×
1414
    if (alloc_size < SIXEL_PALETTE_MAX_DECODER) {
×
1415
        /* memory access range should be 0 <= 255 */
1416
        alloc_size = SIXEL_PALETTE_MAX_DECODER;
1417
    }
1418
    *palette = (unsigned char *)sixel_allocator_malloc(
×
1419
        allocator,
1420
        (size_t)(alloc_size * 3));
×
1421
    if (palette == NULL) {
×
1422
        sixel_allocator_free(allocator, image.pixels.p);
1423
        sixel_helper_set_additional_message(
1424
            "sixel_deocde_raw: sixel_allocator_malloc() failed.");
1425
        status = SIXEL_BAD_ALLOCATION;
1426
        goto error;
1427
    }
1428
    /*
1429
     * Copy the full palette table so default entries remain initialized.
1430
     * This keeps unused slots valid when the image uses fewer colors.
1431
     */
1432
    for (n = 0; n < alloc_size; ++n) {
×
1433
        (*palette)[n * 3 + 0] = image.palette[n] >> 16 & 0xff;
×
1434
        (*palette)[n * 3 + 1] = image.palette[n] >> 8 & 0xff;
×
1435
        (*palette)[n * 3 + 2] = image.palette[n] & 0xff;
×
1436
    }
1437

1438
    *pwidth = image.width;
×
1439
    *pheight = image.height;
×
1440
    *pixels = image.pixels.in_shorts;
×
1441

1442
    status = SIXEL_OK;
×
1443
    goto end;
×
1444

1445
error:
1446
    if (image.pixels.p != NULL) {
×
1447
        if (allocator != NULL) {
×
1448
            sixel_allocator_free(allocator, image.pixels.p);
×
1449
        } else {
1450
            free(image.pixels.p);
×
1451
        }
1452
        image.pixels.p = NULL;
×
1453
    }
1454

1455
end:
1456
    sixel_allocator_unref(allocator);
×
1457
    return status;
×
1458
}
1459

1460

1461
SIXELAPI SIXELSTATUS
1462
sixel_decode_direct(
81✔
1463
    unsigned char       *p,
1464
    int                  len,
1465
    unsigned char      **pixels,
1466
    int                 *pwidth,
1467
    int                 *pheight,
1468
    sixel_allocator_t   *allocator)
1469
{
1470
    SIXELSTATUS status = SIXEL_FALSE;
81✔
1471
    parser_context_t context;
58✔
1472
    image_buffer_t image;
58✔
1473

1474
    image.pixels.p = NULL;
81✔
1475

1476
    if (allocator) {
81!
1477
        sixel_allocator_ref(allocator);
81✔
1478
    } else {
46✔
1479
        status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
×
1480
        if (SIXEL_FAILED(status)) {
×
1481
            allocator = NULL;
×
1482
            goto error;
×
1483
        }
1484
    }
1485

1486
    status = sixel_decode_image(p,
127✔
1487
                                len,
46✔
1488
                                1,
1489
                                1,
1490
                                4U,
1491
                                &image,
1492
                                &context,
1493
                                allocator);
46✔
1494
    if (SIXEL_FAILED(status)) {
81!
1495
        goto error;
×
1496
    }
1497

1498
    *pwidth = image.width;
81✔
1499
    *pheight = image.height;
81✔
1500
    *pixels = image.pixels.in_bytes;
81✔
1501

1502
    status = SIXEL_OK;
81✔
1503
    goto end;
81✔
1504

1505
error:
1506
    if (image.pixels.p != NULL) {
×
1507
        if (allocator != NULL) {
×
1508
            sixel_allocator_free(allocator, image.pixels.p);
×
1509
        } else {
1510
            free(image.pixels.p);
×
1511
        }
1512
        image.pixels.p = NULL;
×
1513
    }
1514

1515
end:
1516
    sixel_allocator_unref(allocator);
81✔
1517
    return status;
104✔
1518
}
23✔
1519

1520

1521
/* deprecated */
1522
SIXELAPI SIXELSTATUS
1523
sixel_decode(unsigned char              /* in */   *p,        /* sixel bytes */
×
1524
             int                        /* in */    len,      /* size of sixel bytes */
1525
             unsigned char              /* out */ **pixels,   /* decoded pixels */
1526
             int                        /* out */  *pwidth,   /* image width */
1527
             int                        /* out */  *pheight,  /* image height */
1528
             unsigned char              /* out */ **palette,  /* ARGB palette */
1529
             int                        /* out */  *ncolors,  /* palette size (<= 256) */
1530
    sixel_allocator_function   /* in */  fn_malloc)  /* malloc function */
1531
{
1532
    SIXELSTATUS status = SIXEL_FALSE;
×
1533
    sixel_allocator_t *allocator = NULL;
×
1534
    parser_context_t context;
1535
    image_buffer_t image;
1536
    int n;
1537

1538
    status = sixel_allocator_new(&allocator, fn_malloc, NULL, NULL, NULL);
×
1539
    if (SIXEL_FAILED(status)) {
×
1540
        allocator = NULL;
×
1541
        goto end;
×
1542
    }
1543

1544
    status = sixel_decode_image(p,
×
1545
                                len,
1546
                                2048,
1547
                                2048,
1548
                                0,
1549
                                &image,
1550
                                &context,
1551
                                allocator);
1552
    if (SIXEL_FAILED(status)) {
×
1553
        goto end;
×
1554
    }
1555

1556
    *ncolors = image.ncolors;
×
1557
    *palette = (unsigned char *)sixel_allocator_malloc(allocator, (size_t)(*ncolors * 3));
×
1558
    if (palette == NULL) {
×
1559
        sixel_allocator_free(allocator, image.pixels.p);
1560
        sixel_helper_set_additional_message(
1561
            "sixel_deocde_raw: sixel_allocator_malloc() failed.");
1562
        status = SIXEL_BAD_ALLOCATION;
1563
        goto end;
1564
    }
1565
    for (n = 0; n < *ncolors; ++n) {
×
1566
        (*palette)[n * 3 + 0] = image.palette[n] >> 16 & 0xff;
×
1567
        (*palette)[n * 3 + 1] = image.palette[n] >> 8 & 0xff;
×
1568
        (*palette)[n * 3 + 2] = image.palette[n] & 0xff;
×
1569
    }
1570

1571
    *pwidth = image.width;
×
1572
    *pheight = image.height;
×
1573
    *pixels = image.pixels.p;
×
1574

1575
    status = SIXEL_OK;
×
1576

1577
end:
1578
    sixel_allocator_unref(allocator);
×
1579
    return status;
×
1580
}
1581

1582
/* emacs Local Variables:      */
1583
/* emacs mode: c               */
1584
/* emacs tab-width: 4          */
1585
/* emacs indent-tabs-mode: nil */
1586
/* emacs c-basic-offset: 4     */
1587
/* emacs End:                  */
1588
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1589
/* 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