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

saitoha / libsixel / 20564600453

29 Dec 2025 03:58AM UTC coverage: 57.322% (-0.6%) from 57.909%
20564600453

push

github

saitoha
Merge branch 'tests/add_missing_tests' into develop

14331 of 44427 branches covered (32.26%)

4 of 9 new or added lines in 2 files covered. (44.44%)

3037 existing lines in 16 files now uncovered.

25159 of 43891 relevant lines covered (57.32%)

4506872.13 hits per line

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

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

24
#if defined(HAVE_CONFIG_H)
25
#include "config.h"
26
#endif
27

28
/* STDC_HEADERS */
29
#include <limits.h>
30
#include <stdint.h>
31
#include <stdlib.h>
32
#include <string.h>
33

34
#include <sixel.h>
35

36
#include "filter-sample.h"
37
#include "filter.h"
38
#include "pixelformat.h"
39

40
typedef struct sixel_filter_sample_state {
41
    sixel_filter_sample_config_t config;
42
} sixel_filter_sample_state_t;
43

44
static size_t
45
sixel_filter_sample_select_stride(
49✔
46
        sixel_filter_sample_config_t const *config,
47
        int width,
48
        int height)
49
{
50
    size_t stride;
49✔
51
    size_t base_target;
49✔
52
    size_t color_budget;
49✔
53
    size_t target;
49✔
54
    size_t total;
49✔
55

56
    stride = 1u;
49✔
57
    base_target = 4096u;
49✔
58
    color_budget = 0u;
49✔
59
    target = base_target;
49✔
60
    total = 0u;
49✔
61

62
    if (width <= 0 || height <= 0) {
49!
63
        return stride;
64
    }
65

66
    if (config != NULL && config->palette_sample_override != 0) {
49!
67
        target = config->palette_sample_target;
6✔
68
    } else {
1✔
69
        if (config != NULL && config->reqcolors > 0) {
43!
70
            color_budget = (size_t)config->reqcolors * 64u;
39✔
71
            if (color_budget / 64u != (size_t)config->reqcolors) {
39!
72
                color_budget = base_target;
73
            }
74
            if (color_budget > target) {
39✔
75
                target = color_budget;
38✔
76
            }
6✔
77
        }
7✔
78

79
        if (config != NULL
47!
80
                && (config->quality_mode == SIXEL_QUALITY_HIGH
43!
81
                    || config->quality_mode == SIXEL_QUALITY_HIGHCOLOR)) {
43✔
82
            if (target <= SIZE_MAX / 2u) {
4!
83
                target *= 2u;
4✔
84
            } else {
1✔
85
                target = SIZE_MAX;
86
            }
87
        } else if (config != NULL
40!
88
                && config->quality_mode == SIXEL_QUALITY_FULL) {
39!
89
            if (target <= SIZE_MAX / 4u) {
3!
90
                target *= 4u;
3✔
91
            } else {
92
                target = SIZE_MAX;
93
            }
94
        }
95
    }
96

97
    total = (size_t)width * (size_t)height;
49✔
98
    while (stride < total && total / (stride * stride) > target) {
365!
99
        ++stride;
316✔
100
    }
101

102
    return stride;
9✔
103
}
9✔
104

105
static SIXELSTATUS
106
sixel_filter_sample_copy_frame(
49✔
107
        sixel_filter_sample_config_t const *config,
108
        sixel_frame_t *frame,
109
        sixel_allocator_t *allocator,
110
        sixel_frame_t **sample_out,
111
        sixel_logger_t *logger,
112
        int *sample_width_out,
113
        int *sample_height_out)
114
{
115
    SIXELSTATUS status;
49✔
116
    sixel_frame_t *sample;
49✔
117
    unsigned char *src_pixels;
49✔
118
    unsigned char *dst_pixels;
49✔
119
    size_t stride;
49✔
120
    int clip_x;
49✔
121
    int clip_y;
49✔
122
    int clip_w;
49✔
123
    int clip_h;
49✔
124
    int src_width;
49✔
125
    int src_height;
49✔
126
    int width;
49✔
127
    int height;
49✔
128
    int depth;
49✔
129
    int sample_width;
49✔
130
    int sample_height;
49✔
131
    size_t sample_count;
49✔
132
    size_t payload_size;
49✔
133
    size_t dst_index;
49✔
134
    size_t src_offset;
49✔
135
    int x;
49✔
136
    int y;
49✔
137

138
    status = SIXEL_FALSE;
49✔
139
    sample = NULL;
49✔
140
    src_pixels = NULL;
49✔
141
    dst_pixels = NULL;
49✔
142
    stride = 1u;
49✔
143
    clip_x = 0;
49✔
144
    clip_y = 0;
49✔
145
    clip_w = 0;
49✔
146
    clip_h = 0;
49✔
147
    src_width = 0;
49✔
148
    src_height = 0;
49✔
149
    width = 0;
49✔
150
    height = 0;
49✔
151
    depth = 0;
49✔
152
    sample_width = 0;
49✔
153
    sample_height = 0;
49✔
154
    sample_count = 0u;
49✔
155
    payload_size = 0u;
49✔
156
    dst_index = 0u;
49✔
157
    src_offset = 0u;
49✔
158
    x = 0;
49✔
159
    y = 0;
49✔
160

161
    if (frame == NULL || sample_out == NULL) {
49!
162
        return SIXEL_BAD_ARGUMENT;
163
    }
164
    if ((sixel_frame_get_pixelformat(frame) & SIXEL_FORMATTYPE_PALETTE)) {
49!
165
        return SIXEL_FEATURE_ERROR;
166
    }
167

168
    src_pixels = sixel_frame_get_pixels(frame);
49✔
169
    src_width = sixel_frame_get_width(frame);
49✔
170
    src_height = sixel_frame_get_height(frame);
49✔
171
    depth = sixel_helper_compute_depth(sixel_frame_get_pixelformat(frame));
49✔
172

173
    if (depth <= 0 || src_pixels == NULL) {
49!
174
        return SIXEL_BAD_ARGUMENT;
175
    }
176

177
    /*
178
     * The logger is currently unused, but it will be wired once planner
179
     * driven progress messages are emitted from the filter layer.
180
     */
181
    (void)logger;
49✔
182

183
    if (config != NULL) {
49!
184
        clip_x = config->clip_x;
49✔
185
        clip_y = config->clip_y;
49✔
186
        clip_w = config->clip_width;
49✔
187
        clip_h = config->clip_height;
49✔
188
    }
9✔
189

190
    if (clip_w <= 0 || clip_h <= 0) {
49!
191
        clip_x = 0;
8✔
192
        clip_y = 0;
8✔
193
        clip_w = src_width;
8✔
194
        clip_h = src_height;
8✔
195
    } else {
8✔
196
        if (clip_w + clip_x > src_width) {
6!
197
            if (clip_x > src_width) {
×
198
                clip_w = 0;
199
            } else {
200
                clip_w = src_width - clip_x;
×
201
            }
202
        }
203

204
        if (clip_h + clip_y > src_height) {
6!
205
            if (clip_y > src_height) {
×
206
                clip_h = 0;
207
            } else {
208
                clip_h = src_height - clip_y;
×
209
            }
210
        }
211

212
        if (clip_w <= 0 || clip_h <= 0) {
6!
213
            return SIXEL_BAD_ARGUMENT;
214
        }
215
    }
216

217
    width = clip_w;
49✔
218
    height = clip_h;
49✔
219

220
    stride = sixel_filter_sample_select_stride(config, width, height);
49✔
221
    sample_width = (width + (int)stride - 1) / (int)stride;
49✔
222
    sample_height = (height + (int)stride - 1) / (int)stride;
49✔
223
    if (sample_width <= 0 || sample_height <= 0) {
49!
224
        return SIXEL_BAD_ARGUMENT;
225
    }
226

227
    if (sample_width_out != NULL) {
49!
228
        *sample_width_out = sample_width;
×
229
    }
230
    if (sample_height_out != NULL) {
49✔
231
        *sample_height_out = sample_height;
12✔
232
    }
2✔
233

234
    sample_count = (size_t)sample_width * (size_t)sample_height;
49✔
235
    if (sample_count != 0u
49!
236
            && sample_count / (size_t)sample_height != (size_t)sample_width) {
49!
237
        return SIXEL_RUNTIME_ERROR;
238
    }
239

240
    payload_size = sample_count * (size_t)depth;
49✔
241
    if (sample_count != 0u && payload_size / sample_count != (size_t)depth) {
49!
242
        return SIXEL_RUNTIME_ERROR;
243
    }
244

245
    status = sixel_frame_new(&sample, allocator);
49✔
246
    if (SIXEL_FAILED(status)) {
49!
247
        return status;
248
    }
249
    dst_pixels = (unsigned char *)sixel_allocator_malloc(allocator,
58✔
250
                                                         payload_size);
9✔
251
    if (dst_pixels == NULL) {
49!
252
        sixel_frame_unref(sample);
×
253
        return SIXEL_BAD_ALLOCATION;
×
254
    }
255

256
    sample->pixels.u8ptr = dst_pixels;
49✔
257
    sample->width = sample_width;
49✔
258
    sample->height = sample_height;
49✔
259
    sample->pixelformat = sixel_frame_get_pixelformat(frame);
49✔
260
    sample->ncolors = (-1);
49✔
261

262
    dst_index = 0u;
49✔
263
    for (y = 0; y < height; y += (int)stride) {
2,698✔
264
        for (x = 0; x < width; x += (int)stride) {
431,417✔
265
            src_offset = ((size_t)(clip_y + y) * (size_t)src_width
473,902✔
266
                       + (size_t)(clip_x + x))
428,768✔
267
                       * (size_t)depth;
428,768✔
268
            memcpy(dst_pixels + dst_index * (size_t)depth,
428,768✔
269
                   src_pixels + src_offset,
383,634✔
270
                   (size_t)depth);
271
            ++dst_index;
428,768✔
272
        }
45,134✔
273

274
    }
408✔
275

276
    *sample_out = sample;
49✔
277

278
    return SIXEL_OK;
49✔
279
}
9✔
280

281
SIXELSTATUS
282
sixel_filter_sample_frame(const sixel_filter_sample_config_t *config,
37✔
283
                          sixel_frame_t *frame,
284
                          sixel_allocator_t *allocator,
285
                          sixel_frame_t **sample_out,
286
                          sixel_logger_t *logger)
287
{
288
    SIXELSTATUS status;
37✔
289

290
    status = sixel_filter_sample_copy_frame(config, frame, allocator,
44✔
291
                                            sample_out, logger, NULL, NULL);
7✔
292

293
    return status;
44✔
294
}
7✔
295

296
static SIXELSTATUS
297
sixel_filter_sample_apply(sixel_filter_t *filter,
12✔
298
                          sixel_allocator_t *allocator,
299
                          sixel_logger_t *logger)
300
{
301
    SIXELSTATUS status;
12✔
302
    sixel_filter_sample_state_t *state;
12✔
303
    sixel_frame_t *input_frame;
12✔
304
    sixel_frame_t *sample;
12✔
305
    int sample_height;
12✔
306

307
    if (filter == NULL || allocator == NULL) {
12!
308
        return SIXEL_BAD_ARGUMENT;
309
    }
310

311
    state = (sixel_filter_sample_state_t *)filter->userdata;
12✔
312
    if (state == NULL) {
12!
313
        return SIXEL_BAD_ARGUMENT;
314
    }
315

316
    if (filter->input.slot == NULL || filter->output.slot == NULL) {
12!
317
        return SIXEL_BAD_ARGUMENT;
318
    }
319

320
    input_frame = *(filter->input.slot);
12✔
321
    if (input_frame == NULL) {
12!
322
        return SIXEL_BAD_ARGUMENT;
323
    }
324

325
    if (*(filter->output.slot) != NULL) {
12!
UNCOV
326
        sixel_frame_unref(*(filter->output.slot));
×
327
        *(filter->output.slot) = NULL;
×
328
    }
329

330
    sample_height = 0;
12✔
331
    status = sixel_filter_sample_copy_frame(&state->config, input_frame,
14✔
332
                                            allocator, &sample, logger,
2✔
333
                                            NULL, &sample_height);
334
    if (SIXEL_SUCCEEDED(status)) {
12!
335
        *(filter->output.slot) = sample;
12✔
336
        if (sample_height > 0) {
12!
337
            filter->progress.total_units = sample_height;
12✔
338
            filter->progress.completed_units = sample_height;
12✔
339
            sixel_filter_update_progress(filter, sample_height);
12✔
340
        }
2✔
341
    }
2✔
342

343
    return status;
2✔
344
}
2✔
345

346
static void
347
sixel_filter_sample_dispose(sixel_filter_t *filter)
12✔
348
{
349
    sixel_filter_sample_state_t *state;
12✔
350

351
    if (filter == NULL) {
12!
352
        return;
353
    }
354

355
    state = (sixel_filter_sample_state_t *)filter->userdata;
12✔
356
    if (state != NULL) {
12!
357
        free(state);
12✔
358
    }
2✔
359
}
2!
360

361
SIXELSTATUS
362
sixel_filter_sample_init(sixel_filter_t *filter,
12✔
363
                         const sixel_filter_sample_config_t *config)
364
{
365
    SIXELSTATUS status;
12✔
366
    sixel_filter_sample_state_t *state;
12✔
367

368
    if (filter == NULL || config == NULL) {
12!
369
        return SIXEL_BAD_ARGUMENT;
370
    }
371

372
    state = (sixel_filter_sample_state_t *)malloc(sizeof(*state));
12✔
373
    if (state == NULL) {
12!
374
        return SIXEL_BAD_ALLOCATION;
375
    }
376

377
    state->config = *config;
12✔
378

379
    status = sixel_filter_init(filter,
14✔
380
                               "sample",
381
                               SIXEL_FILTER_KIND_SAMPLE,
382
                               sixel_filter_sample_apply,
383
                               sixel_filter_sample_dispose,
384
                               state);
2✔
385
    if (SIXEL_FAILED(status)) {
12!
UNCOV
386
        free(state);
×
387
    }
388

389
    return status;
2✔
390
}
2✔
391

392
/* emacs Local Variables:      */
393
/* emacs mode: c               */
394
/* emacs tab-width: 4          */
395
/* emacs indent-tabs-mode: nil */
396
/* emacs c-basic-offset: 4     */
397
/* emacs End:                  */
398
/* vim: set expandtab ts=4 sts=4 sw=4 : */
399
/* 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