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

saitoha / libsixel / 22281294763

22 Feb 2026 04:53PM UTC coverage: 81.971% (-1.7%) from 83.691%
22281294763

push

github

saitoha
tests: enforce SIXEL_TEST_PYTHON contract for python tap runs

27660 of 54661 branches covered (50.6%)

45511 of 55521 relevant lines covered (81.97%)

2582260.61 hits per line

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

81.82
/src/loader-libjpeg.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
 * libjpeg-backed loader helpers extracted from loader.c to prepare for
26
 * backend-specific translation units. The functions here remain thin wrappers
27
 * around the shared callbacks and frame utilities so the registration table
28
 * can reference them without pulling libjpeg headers into unrelated code.
29
 */
30

31
#if defined(HAVE_CONFIG_H)
32
#include "config.h"
33
#endif
34

35
#if HAVE_JPEG
36

37
#include <stdio.h>
38
#if HAVE_STRING_H
39
# include <string.h>
40
#endif
41
#if HAVE_ERRNO_H
42
# include <errno.h>
43
#endif
44
#if HAVE_LIMITS_H
45
# include <limits.h>
46
#endif
47
#include <jpeglib.h>
48

49
#include <sixel.h>
50

51
#include "allocator.h"
52
#include "chunk.h"
53
#include "loader-common.h"
54
#include "frame.h"
55
#include "loader-libjpeg.h"
56
#include "logger.h"
57

58
/*
59
 * import from @uobikiemukot's sdump loader.h
60
 *
61
 * The helper keeps libjpeg-specific state localized so only this file needs
62
 * to include jpeglib.h. Callers receive raw RGB buffers and metadata filled
63
 * through the OUT parameters.
64
 */
65
static SIXELSTATUS
66
load_jpeg(unsigned char **result,
58✔
67
          unsigned char *data,
68
          size_t datasize,
69
          int *pwidth,
70
          int *pheight,
71
          int *ppixelformat,
72
          sixel_allocator_t *allocator)
73
{
74
    SIXELSTATUS status;
58✔
75
    JDIMENSION row_stride;
58✔
76
    size_t size;
58✔
77
    JSAMPARRAY buffer;
58✔
78
    struct jpeg_decompress_struct cinfo;
58✔
79
    struct jpeg_error_mgr pub;
58✔
80

81
    status = SIXEL_JPEG_ERROR;
58✔
82
    cinfo.err = jpeg_std_error(&pub);
58✔
83

84
    jpeg_create_decompress(&cinfo);
58✔
85
    jpeg_mem_src(&cinfo, data, datasize);
58✔
86
    jpeg_read_header(&cinfo, TRUE);
58✔
87

88
    /* disable colormap (indexed color), grayscale -> rgb */
89
    cinfo.quantize_colors = FALSE;
58✔
90
    cinfo.out_color_space = JCS_RGB;
58✔
91
    jpeg_start_decompress(&cinfo);
58✔
92

93
    if (cinfo.output_components != 3) {
58!
94
        sixel_helper_set_additional_message(
×
95
            "load_jpeg: unknown pixel format.");
96
        status = SIXEL_BAD_INPUT;
×
97
        goto end;
×
98
    }
99

100
    *ppixelformat = SIXEL_PIXELFORMAT_RGB888;
58✔
101

102
    if (cinfo.output_width > INT_MAX || cinfo.output_height > INT_MAX) {
58!
103
        status = SIXEL_BAD_INTEGER_OVERFLOW;
×
104
        goto end;
×
105
    }
106
    *pwidth = (int)cinfo.output_width;
58✔
107
    *pheight = (int)cinfo.output_height;
58✔
108

109
    size = (size_t)(*pwidth * *pheight * cinfo.output_components);
58✔
110
    *result = (unsigned char *)
58✔
111
        sixel_allocator_malloc(allocator, size);
58✔
112
    if (*result == NULL) {
58!
113
        sixel_helper_set_additional_message(
×
114
            "load_jpeg: sixel_allocator_malloc() failed.");
115
        status = SIXEL_BAD_ALLOCATION;
×
116
        goto end;
×
117
    }
118
    row_stride = cinfo.output_width * (unsigned int)cinfo.output_components;
58✔
119
    buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo,
116✔
120
                                        JPOOL_IMAGE,
121
                                        row_stride,
58✔
122
                                        1);
123

124
    while (cinfo.output_scanline < cinfo.output_height) {
5,990✔
125
        jpeg_read_scanlines(&cinfo, buffer, 1);
5,932✔
126
        if (cinfo.err->num_warnings > 0) {
5,932!
127
            sixel_helper_set_additional_message(
×
128
                "jpeg_read_scanlines: error/warining occuered.");
129
            status = SIXEL_BAD_INPUT;
×
130
            goto end;
×
131
        }
132
        memcpy(*result + (cinfo.output_scanline - 1) * row_stride,
5,932✔
133
               buffer[0],
134
               row_stride);
135
    }
136

137
    status = SIXEL_OK;
58✔
138

139
end:
140
    jpeg_finish_decompress(&cinfo);
58✔
141
    jpeg_destroy_decompress(&cinfo);
58✔
142

143
    return status;
116✔
144
}
58✔
145

146
/*
147
 * Dedicated libjpeg loader wiring minimal pipeline.
148
 *
149
 *    +------------+     +-------------------+     +--------------------+
150
 *    | JPEG chunk | --> | libjpeg decode    | --> | sixel frame emit   |
151
 *    +------------+     +-------------------+     +--------------------+
152
 */
153
SIXELSTATUS
154
load_with_libjpeg(
58✔
155
    sixel_chunk_t const       /* in */     *pchunk,
156
    int                       /* in */     fstatic,
157
    int                       /* in */     fuse_palette,
158
    int                       /* in */     reqcolors,
159
    unsigned char             /* in */     *bgcolor,
160
    int                       /* in */     loop_control,
161
    int                       /* in */     start_frame_no_set,
162
    int                       /* in */     start_frame_no,
163
    sixel_load_image_function /* in */     fn_load,
164
    void                      /* in/out */ *context)
165
{
166
    SIXELSTATUS status;
58✔
167
    sixel_frame_t *frame;
58✔
168
    unsigned char *pixels;
58✔
169

170
    status = SIXEL_FALSE;
58✔
171
    frame = NULL;
58✔
172
    pixels = NULL;
58✔
173

174
    (void)fstatic;
58✔
175
    (void)fuse_palette;
58✔
176
    (void)reqcolors;
58✔
177
    (void)loop_control;
58✔
178
    (void)start_frame_no_set;
58✔
179
    (void)start_frame_no;
58✔
180

181
    status = sixel_frame_new(&frame, pchunk->allocator);
58✔
182
    if (SIXEL_FAILED(status)) {
58!
183
        goto end;
×
184
    }
185

186
    status = load_jpeg(&pixels,
58✔
187
                       pchunk->buffer,
58✔
188
                       pchunk->size,
58✔
189
                       &frame->width,
58✔
190
                       &frame->height,
58✔
191
                       &frame->pixelformat,
58✔
192
                       pchunk->allocator);
58✔
193
    if (SIXEL_FAILED(status)) {
58!
194
        goto end;
×
195
    }
196

197
    sixel_frame_set_pixels(frame, pixels);
58✔
198

199
    status = sixel_frame_strip_alpha(frame, bgcolor);
58✔
200
    if (SIXEL_FAILED(status)) {
58!
201
        goto end;
×
202
    }
203

204
    status = fn_load(frame, context);
58✔
205
    if (SIXEL_FAILED(status)) {
58!
206
        goto end;
×
207
    }
208

209
    status = SIXEL_OK;
58✔
210

211
end:
212
    sixel_frame_unref(frame);
58✔
213

214
    return status;
116✔
215
}
58✔
216

217
int
218
loader_can_try_libjpeg(sixel_chunk_t const *chunk)
901✔
219
{
220
    if (chunk == NULL) {
901!
221
        return 0;
×
222
    }
223

224
    return chunk_is_jpeg(chunk);
901✔
225
}
901✔
226

227
#else  /* !HAVE_JPEG */
228

229
/*
230
 * Keep a harmless placeholder around so pedantic builds skip the empty unit
231
 * warning when libjpeg is not part of the build.
232
 */
233
enum { sixel_loader_libjpeg_placeholder = 0 };
234

235
#if defined(__GNUC__) || defined(__clang__)
236
# define SIXEL_LIBJPEG_PLACEHOLDER_UNUSED __attribute__((unused))
237
#else
238
# define SIXEL_LIBJPEG_PLACEHOLDER_UNUSED
239
#endif
240

241
static void
242
sixel_loader_libjpeg_placeholder_function(void)
243
    SIXEL_LIBJPEG_PLACEHOLDER_UNUSED;
244

245
static void
246
sixel_loader_libjpeg_placeholder_function(void)
247
{
248
    /*
249
     * The placeholder ties the enum to a symbol so MSVC does not warn about
250
     * an empty translation unit when libjpeg support is disabled.
251
     */
252
    (void)sixel_loader_libjpeg_placeholder;
253
}
254

255
#undef SIXEL_LIBJPEG_PLACEHOLDER_UNUSED
256

257
#endif  /* HAVE_JPEG */
258

259
/* emacs Local Variables:      */
260
/* emacs mode: c               */
261
/* emacs tab-width: 4          */
262
/* emacs indent-tabs-mode: nil */
263
/* emacs c-basic-offset: 4     */
264
/* emacs End:                  */
265
/* vim: set expandtab ts=4 sts=4 sw=4 : */
266
/* 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