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

saitoha / libsixel / 22614338486

03 Mar 2026 08:17AM UTC coverage: 84.041%. First build
22614338486

push

github

saitoha
coregraphics: guard CFNumberGetValue results in GIF metadata

29951 of 57496 branches covered (52.09%)

14 of 21 new or added lines in 1 file covered. (66.67%)

51209 of 60933 relevant lines covered (84.04%)

4336368.32 hits per line

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

79.71
/src/loader-coregraphics.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
 * CoreGraphics-backed loader extracted from loader.c. Isolating macOS headers
26
 * keeps other backends lightweight while preserving the existing decoding
27
 * sequence and diagnostics.
28
 */
29

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

34
#if HAVE_COREGRAPHICS
35

36
#include <stdio.h>
37
#include <limits.h>
38
#include <stdlib.h>
39

40
#if HAVE_STRING_H
41
# include <string.h>
42
#endif
43

44
#include <ApplicationServices/ApplicationServices.h>
45
#include <ImageIO/ImageIO.h>
46

47
#include <sixel.h>
48

49
#include "chunk.h"
50
#include "frame.h"
51
#include "loader-coregraphics.h"
52
#include "loader.h"
53
#include "logger.h"
54
#include "compat_stub.h"
55

56
typedef struct sixel_loader_coregraphics_component {
57
    sixel_loader_component_t base;
58
    sixel_allocator_t *allocator;
59
    unsigned int ref;
60
    int fstatic;
61
    int fuse_palette;
62
    int reqcolors;
63
    unsigned char bgcolor[3];
64
    int has_bgcolor;
65
    int loop_control;
66
    int has_start_frame_no;
67
    int start_frame_no;
68
} sixel_loader_coregraphics_component_t;
69

70

71
static SIXELSTATUS
72
coregraphics_parse_animation_start_frame_no(int *start_frame_no)
544✔
73
{
74
    SIXELSTATUS status;
340✔
75
    char const *env_value;
340✔
76
    char *endptr;
340✔
77
    long parsed;
340✔
78

79
    status = SIXEL_OK;
544✔
80
    env_value = NULL;
544✔
81
    endptr = NULL;
544✔
82
    parsed = 0;
544✔
83

84
    *start_frame_no = INT_MIN;
544✔
85
    env_value = sixel_compat_getenv("SIXEL_LOADER_ANIMATION_START_FRAME_NO");
544✔
86
    if (env_value == NULL || env_value[0] == '\0') {
544!
87
        goto end;
544✔
88
    }
89

90
    parsed = strtol(env_value, &endptr, 10);
×
91
    if (endptr == env_value || *endptr != '\0') {
×
92
        sixel_helper_set_additional_message(
×
93
            "SIXEL_LOADER_ANIMATION_START_FRAME_NO must be an integer.");
94
        status = SIXEL_BAD_INPUT;
×
95
        goto end;
×
96
    }
97
    if (parsed < (long)INT_MIN || parsed > (long)INT_MAX) {
×
98
        sixel_helper_set_additional_message(
×
99
            "SIXEL_LOADER_ANIMATION_START_FRAME_NO is out of range.");
100
        status = SIXEL_BAD_INPUT;
×
101
        goto end;
×
102
    }
103

104
    *start_frame_no = (int)parsed;
×
105

106
end:
107
    return status;
884✔
108
}
340✔
109

110
static SIXELSTATUS
111
coregraphics_resolve_animation_start_frame_no(int start_frame_no,
48✔
112
                                              int frame_count,
113
                                              int *resolved)
114
{
115
    SIXELSTATUS status;
30✔
116
    int index;
30✔
117

118
    status = SIXEL_OK;
48✔
119
    index = 0;
48✔
120

121
    if (frame_count <= 0) {
48!
122
        sixel_helper_set_additional_message(
×
123
            "Animation frame count must be positive.");
124
        status = SIXEL_BAD_INPUT;
×
125
        goto end;
×
126
    }
127

128
    if (start_frame_no >= 0) {
48✔
129
        index = start_frame_no;
32✔
130
    } else {
32✔
131
        index = frame_count + start_frame_no;
16✔
132
    }
133

134
    if (index < 0 || index >= frame_count) {
48✔
135
        sixel_helper_set_additional_message(
16✔
136
            "SIXEL_LOADER_ANIMATION_START_FRAME_NO is outside"
137
            " the animation frame range.");
138
        status = SIXEL_BAD_INPUT;
16✔
139
        goto end;
16✔
140
    }
141

142
    *resolved = index;
32✔
143

144
end:
145
    return status;
78✔
146
}
30✔
147

148
static SIXELSTATUS
149
load_with_coregraphics(
592✔
150
    sixel_chunk_t const       /* in */     *pchunk,
151
    int                       /* in */     fstatic,
152
    int                       /* in */     fuse_palette,
153
    int                       /* in */     reqcolors,
154
    unsigned char             /* in */     *bgcolor,
155
    int                       /* in */     loop_control,
156
    int                       /* in */     start_frame_no_set,
157
    int                       /* in */     start_frame_no_override,
158
    sixel_load_image_function /* in */     fn_load,
159
    void                      /* in/out */ *context)
160
{
161
    SIXELSTATUS status = SIXEL_FALSE;
592✔
162
    sixel_frame_t *frame = NULL;
592✔
163
    CFDataRef data = NULL;
592✔
164
    CGImageSourceRef source = NULL;
592✔
165
    CGImageRef image = NULL;
592✔
166
    CGColorSpaceRef color_space = NULL;
592✔
167
    CGContextRef ctx = NULL;
592✔
168
    size_t stride;
370✔
169
    size_t frame_count;
370✔
170
    int anim_loop_count = (-1);
592✔
171
    CFDictionaryRef props = NULL;
592✔
172
    CFDictionaryRef anim_dict;
370✔
173
    CFNumberRef loop_num;
370✔
174
    CFDictionaryRef frame_props;
370✔
175
    CFDictionaryRef frame_anim_dict;
370✔
176
    CFNumberRef delay_num;
370✔
177
    unsigned char *pixels;
370✔
178
    int start_frame_no;
370✔
179
    int resolved_start_frame_no;
370✔
180
    int total_frames;
370✔
181
    int frame_index;
370✔
182
    int frames_in_loop;
370✔
183
    int loop_no;
370✔
184
    int stop_loop;
370✔
185
    int is_animation_container;
370✔
186

187
    (void) fuse_palette;
592✔
188
    (void) reqcolors;
592✔
189
    (void) bgcolor;
592✔
190

191
    start_frame_no = INT_MIN;
592✔
192
    resolved_start_frame_no = INT_MIN;
592✔
193
    total_frames = 0;
592✔
194
    frame_index = 0;
592✔
195
    frames_in_loop = 0;
592✔
196
    loop_no = 0;
592✔
197
    stop_loop = 0;
592✔
198
    is_animation_container = 0;
592✔
199
    frame_props = NULL;
592✔
200

201
    if (start_frame_no_set) {
592✔
202
        start_frame_no = start_frame_no_override;
48✔
203
    } else {
48✔
204
        status = coregraphics_parse_animation_start_frame_no(&start_frame_no);
544✔
205
        if (SIXEL_FAILED(status)) {
544!
206
            goto end;
×
207
        }
208
    }
209

210
    status = sixel_frame_new(&frame, pchunk->allocator);
592✔
211
    if (SIXEL_FAILED(status)) {
592!
212
        goto end;
×
213
    }
214

215
    data = CFDataCreate(kCFAllocatorDefault,
1,184✔
216
                        pchunk->buffer,
592✔
217
                        (CFIndex)pchunk->size);
592✔
218
    if (! data) {
592✔
219
        status = SIXEL_FALSE;
×
220
        goto end;
×
221
    }
222

223
    source = CGImageSourceCreateWithData(data, NULL);
592✔
224
    if (! source) {
592✔
225
        status = SIXEL_FALSE;
×
226
        goto end;
×
227
    }
228

229
    frame_count = CGImageSourceGetCount(source);
592✔
230
    if (! frame_count) {
592✔
231
        status = SIXEL_FALSE;
104✔
232
        goto end;
104✔
233
    }
234

235
    total_frames = (int)frame_count;
488✔
236
    if (start_frame_no != INT_MIN) {
488✔
237
        status = coregraphics_resolve_animation_start_frame_no(
48✔
238
            start_frame_no,
48✔
239
            total_frames,
48✔
240
            &resolved_start_frame_no);
241
        if (SIXEL_FAILED(status)) {
48✔
242
            goto end;
16✔
243
        }
244
    }
32✔
245

246
    /*
247
     * Keep total_frames as the actual image frame count even in static mode.
248
     * In static mode we still need to seek to resolved_start_frame_no first,
249
     * then emit exactly one frame and return from inside the decode loop.
250
     */
251

252
    props = CGImageSourceCopyProperties(source, NULL);
472✔
253
    if (props) {
472!
254
        anim_dict = (CFDictionaryRef)CFDictionaryGetValue(
472✔
255
            props, kCGImagePropertyGIFDictionary);
472✔
256
        if (anim_dict) {
472✔
257
            /*
258
             * Treat multi-frame decoding as animation only when the source
259
             * exposes GIF animation metadata. A multi-size ICO can contain
260
             * multiple static images and must not enter animation mode.
261
             */
262
            is_animation_container = 1;
136✔
263
            loop_num = (CFNumberRef)CFDictionaryGetValue(
136✔
264
                anim_dict, kCGImagePropertyGIFLoopCount);
136✔
265
            if (loop_num) {
136!
266
                int loop_value;
85✔
267
                Boolean ok;
85✔
268

269
                loop_value = anim_loop_count;
136✔
270
                ok = CFNumberGetValue(loop_num, kCFNumberIntType, &loop_value);
136✔
271
                if (ok) {
136!
272
                    anim_loop_count = loop_value;
136✔
273
                }
136✔
274
            }
136✔
275
        }
136✔
276
    }
472✔
277

278
    frame->multiframe = (!fstatic && frame_count > 1
566✔
279
                        && is_animation_container);
514✔
280

281
    for (;;) {
472✔
282
        frame_index = 0;
472✔
283
        if (loop_no == 0 && resolved_start_frame_no != INT_MIN) {
472!
284
            /*
285
             * Apply start-frame override only on the first loop. Later loops
286
             * always restart from frame 0 to preserve normal replay behavior.
287
             */
288
            frame_index = resolved_start_frame_no;
32✔
289
        }
32✔
290
        frames_in_loop = 0;
472✔
291

292
        while (frame_index < total_frames) {
784✔
293
            frame->frame_no = frames_in_loop;
688✔
294
            frame->loop_count = loop_no;
688✔
295

296
            image = CGImageSourceCreateImageAtIndex(
688✔
297
                source, (unsigned int)frame_index, NULL);
688✔
298
            if (! image) {
688!
299
                status = SIXEL_FALSE;
×
300
                goto end;
×
301
            }
302

303
            frame_props = CGImageSourceCopyPropertiesAtIndex(
688✔
304
                source, (unsigned int)frame_index, NULL);
688✔
305
            if (frame_props) {
688!
306
                frame_anim_dict = (CFDictionaryRef)CFDictionaryGetValue(
688✔
307
                    frame_props, kCGImagePropertyGIFDictionary);
688✔
308
                if (frame_anim_dict) {
688✔
309
                    loop_num = (CFNumberRef)CFDictionaryGetValue(
352✔
310
                        frame_anim_dict, kCGImagePropertyGIFLoopCount);
352✔
311
                    if (loop_num) {
352!
312
                        int loop_value;
313
                        Boolean ok;
314

NEW
315
                        loop_value = anim_loop_count;
×
NEW
316
                        ok = CFNumberGetValue(loop_num,
×
317
                                              kCFNumberIntType,
318
                                              &loop_value);
NEW
319
                        if (ok) {
×
NEW
320
                            anim_loop_count = loop_value;
×
NEW
321
                        }
×
322
                    }
×
323
                    delay_num = (CFNumberRef)CFDictionaryGetValue(
308✔
324
                        frame_anim_dict,
308✔
325
                        kCGImagePropertyGIFUnclampedDelayTime);
308✔
326
                    if (! delay_num) {
308!
327
                        delay_num = (CFNumberRef)CFDictionaryGetValue(
×
328
                            frame_anim_dict, kCGImagePropertyGIFDelayTime);
×
329
                    }
×
330
                    if (delay_num) {
352!
331
                        double delay_value;
220✔
332
                        Boolean ok;
220✔
333

334
                        delay_value = 0.0;
352✔
335
                        ok = CFNumberGetValue(delay_num,
352✔
336
                                              kCFNumberDoubleType,
337
                                              &delay_value);
338
                        if (ok) {
352!
339
                            if (delay_value < 0) {
352!
NEW
340
                                delay_value = 0.0;
×
NEW
341
                            }
×
342
                            frame->delay = (int)(delay_value * 100);
352✔
343
                        }
352✔
344
                    }
352✔
345
                }
352✔
346
                CFRelease(frame_props);
688✔
347
                frame_props = NULL;
688✔
348
            }
688✔
349

350
            frame->width = (int)CGImageGetWidth(image);
688✔
351
            frame->height = (int)CGImageGetHeight(image);
688✔
352
            /*
353
             * CoreGraphics renders into a premultiplied RGBA surface. Report
354
             * the four-component layout so downstream planners know an alpha
355
             * channel is available.
356
             */
357
            frame->pixelformat = SIXEL_PIXELFORMAT_RGBA8888;
688✔
358
            frame->colorspace = SIXEL_COLORSPACE_GAMMA;
688✔
359

360
            if (frame->width > SIXEL_WIDTH_LIMIT) {
688✔
361
                sixel_helper_set_additional_message(
×
362
                    "load_with_coregraphics: given width parameter is too"
363
                    " huge.");
364
                status = SIXEL_BAD_INPUT;
×
365
                goto end;
×
366
            }
367
            if (frame->height > SIXEL_HEIGHT_LIMIT) {
688!
368
                sixel_helper_set_additional_message(
×
369
                    "load_with_coregraphics: given height parameter is too"
370
                    " huge.");
371
                status = SIXEL_BAD_INPUT;
×
372
                goto end;
×
373
            }
374
            if (frame->width <= 0) {
688!
375
                sixel_helper_set_additional_message(
×
376
                    "load_with_coregraphics: an invalid width parameter"
377
                    " detected.");
378
                status = SIXEL_BAD_INPUT;
×
379
                goto end;
×
380
            }
381
            if (frame->height <= 0) {
688!
382
                sixel_helper_set_additional_message(
×
383
                    "load_with_coregraphics: an invalid width parameter"
384
                    " detected.");
385
                status = SIXEL_BAD_INPUT;
×
386
                goto end;
×
387
            }
388
            if (frame->height >= INT_MAX / 4 ||
1,376!
389
                    frame->width >= INT_MAX / 4 ||
688!
390
                    frame->height * frame->width * 4 >= INT_MAX) {
688✔
391
                sixel_helper_set_additional_message(
×
392
                    "load_with_coregraphics: too large image.");
393
                status = SIXEL_RUNTIME_ERROR;
×
394
                goto end;
×
395
            }
396

397
            stride = (size_t)frame->width * 4;
688✔
398
            sixel_frame_set_pixels(frame,
1,376✔
399
                                   sixel_allocator_malloc(
688✔
400
                                       pchunk->allocator,
688✔
401
                                       (size_t)(frame->height * stride)));
688✔
402
            pixels = sixel_frame_get_pixels(frame);
688✔
403
            if (pixels == NULL) {
688✔
404
                sixel_helper_set_additional_message(
×
405
                    "load_with_coregraphics: sixel_allocator_malloc()"
406
                    " failed.");
407
                status = SIXEL_BAD_ALLOCATION;
×
408
                goto end;
×
409
            }
410

411
            color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
688✔
412
            if (! color_space) {
688!
413
                CGImageRelease(image);
×
414
                goto end;
×
415
            }
416

417
            ctx = CGBitmapContextCreate(pixels,
1,376✔
418
                                        (size_t)frame->width,
688✔
419
                                        (size_t)frame->height,
688✔
420
                                        8,
421
                                        stride,
688✔
422
                                        color_space,
688✔
423
                                        kCGImageAlphaPremultipliedLast |
424
                                                kCGBitmapByteOrder32Big);
425
            if (!ctx) {
688✔
426
                CGImageRelease(image);
×
427
                goto end;
×
428
            }
429

430
            CGContextDrawImage(ctx,
1,376✔
431
                               CGRectMake(0, 0, frame->width, frame->height),
688✔
432
                               image);
688✔
433
            CGContextRelease(ctx);
688✔
434
            ctx = NULL;
688✔
435

436
            frame->multiframe = (!fstatic && frame_count > 1
992✔
437
                                && is_animation_container);
730✔
438
            status = fn_load(frame, context);
688✔
439
            CGImageRelease(image);
688✔
440
            image = NULL;
688✔
441
            if (status != SIXEL_OK) {
688✔
442
                goto end;
×
443
            }
444

445
            if (sixel_loader_callback_is_canceled(context)) {
688!
446
                status = SIXEL_INTERRUPTED;
×
447
                goto end;
×
448
            }
449

450
            ++frame_index;
688✔
451
            ++frames_in_loop;
688✔
452

453
            if (fstatic || !is_animation_container) {
688✔
454
                status = SIXEL_OK;
376✔
455
                goto end;
376✔
456
            }
457
        }
458

459
        ++loop_no;
96✔
460
        stop_loop = 0;
96✔
461

462
        if (total_frames <= 1 || loop_control == SIXEL_LOOP_DISABLE) {
96!
463
            stop_loop = 1;
96✔
464
        } else if (loop_control == SIXEL_LOOP_AUTO) {
96!
465
            if (anim_loop_count < 0) {
×
466
                stop_loop = 1;
×
467
            } else if (anim_loop_count > 0 && loop_no >= anim_loop_count) {
×
468
                stop_loop = 1;
×
469
            }
×
470
        }
×
471

472
        if (stop_loop) {
96!
473
            break;
96✔
474
        }
475
    }
476

477
    status = SIXEL_OK;
96✔
478

479
end:
480
    if (ctx) {
592!
481
        CGContextRelease(ctx);
×
482
    }
×
483
    if (color_space) {
577✔
484
        CGColorSpaceRelease(color_space);
472✔
485
    }
472✔
486
    if (image) {
636!
487
        CGImageRelease(image);
×
488
    }
×
489
    if (source) {
592!
490
        CFRelease(source);
592✔
491
    }
592✔
492
    if (props) {
607✔
493
        CFRelease(props);
472✔
494
    }
472✔
495
    if (data) {
592!
496
        CFRelease(data);
592✔
497
    }
592✔
498
    if (frame) {
592!
499
        sixel_frame_unref(frame);
592✔
500
    }
592✔
501
    return status;
962✔
502
}
370✔
503

504

505
static void
506
sixel_loader_coregraphics_ref(sixel_loader_component_t *component)
7,546✔
507
{
508
    sixel_loader_coregraphics_component_t *self;
4,662✔
509

510
    self = NULL;
7,546✔
511
    if (component == NULL) {
7,546✔
512
        return;
×
513
    }
514

515
    self = (sixel_loader_coregraphics_component_t *)component;
7,546✔
516
    ++self->ref;
7,546✔
517
}
7,546!
518

519
static void
520
sixel_loader_coregraphics_unref(sixel_loader_component_t *component)
15,100✔
521
{
522
    sixel_loader_coregraphics_component_t *self;
9,329✔
523
    sixel_allocator_t *allocator;
9,329✔
524

525
    self = NULL;
15,100✔
526
    allocator = NULL;
15,100✔
527
    if (component == NULL) {
15,100✔
528
        return;
×
529
    }
530

531
    self = (sixel_loader_coregraphics_component_t *)component;
15,100✔
532
    if (self->ref == 0u) {
15,100✔
533
        return;
×
534
    }
535

536
    --self->ref;
15,100✔
537
    if (self->ref > 0u) {
15,100✔
538
        return;
7,546✔
539
    }
540

541
    allocator = self->allocator;
7,554✔
542
    sixel_allocator_free(allocator, self);
7,554✔
543
    sixel_allocator_unref(allocator);
7,554✔
544
}
15,100!
545

546
static SIXELSTATUS
547
sixel_loader_coregraphics_setopt(sixel_loader_component_t *component,
3,528✔
548
                                 int option,
549
                                 void const *value)
550
{
551
    sixel_loader_coregraphics_component_t *self;
2,205✔
552
    int const *flag;
2,205✔
553
    unsigned char const *color;
2,205✔
554

555
    self = NULL;
3,528✔
556
    flag = NULL;
3,528✔
557
    color = NULL;
3,528✔
558
    if (component == NULL) {
3,528✔
559
        return SIXEL_BAD_ARGUMENT;
×
560
    }
561

562
    self = (sixel_loader_coregraphics_component_t *)component;
3,528✔
563
    switch (option) {
3,528✔
564
    case SIXEL_LOADER_OPTION_REQUIRE_STATIC:
565
        flag = (int const *)value;
592✔
566
        self->fstatic = flag != NULL ? *flag : 0;
592!
567
        return SIXEL_OK;
592✔
568
    case SIXEL_LOADER_OPTION_USE_PALETTE:
569
        flag = (int const *)value;
592✔
570
        self->fuse_palette = flag != NULL ? *flag : 0;
592!
571
        return SIXEL_OK;
592✔
572
    case SIXEL_LOADER_OPTION_REQCOLORS:
573
        flag = (int const *)value;
592✔
574
        if (flag != NULL) {
592!
575
            self->reqcolors = *flag;
592✔
576
        }
592✔
577
        return SIXEL_OK;
592✔
578
    case SIXEL_LOADER_OPTION_BGCOLOR:
579
        if (value == NULL) {
584✔
580
            self->has_bgcolor = 0;
584✔
581
            return SIXEL_OK;
584✔
582
        }
583
        color = (unsigned char const *)value;
×
584
        self->bgcolor[0] = color[0];
×
585
        self->bgcolor[1] = color[1];
×
586
        self->bgcolor[2] = color[2];
×
587
        self->has_bgcolor = 1;
×
588
        return SIXEL_OK;
×
589
    case SIXEL_LOADER_OPTION_LOOP_CONTROL:
590
        flag = (int const *)value;
584✔
591
        if (flag != NULL) {
584!
592
            self->loop_control = *flag;
584✔
593
        }
584✔
594
        return SIXEL_OK;
584✔
595
    case SIXEL_LOADER_OPTION_START_FRAME_NO:
596
        if (value == NULL) {
584✔
597
            self->has_start_frame_no = 0;
536✔
598
            self->start_frame_no = INT_MIN;
536✔
599
            return SIXEL_OK;
536✔
600
        }
601
        flag = (int const *)value;
48✔
602
        self->start_frame_no = *flag;
48✔
603
        self->has_start_frame_no = 1;
48✔
604
        return SIXEL_OK;
48✔
605
    default:
606
        return SIXEL_OK;
×
607
    }
608
}
3,528✔
609

610
static SIXELSTATUS
611
sixel_loader_coregraphics_load(sixel_loader_component_t *component,
592✔
612
                               sixel_chunk_t const *chunk,
613
                               sixel_load_image_function fn_load,
614
                               void *context)
615
{
616
    sixel_loader_coregraphics_component_t *self;
370✔
617
    unsigned char *bgcolor;
370✔
618

619
    self = NULL;
592✔
620
    bgcolor = NULL;
592✔
621
    if (component == NULL || chunk == NULL || fn_load == NULL) {
592!
622
        return SIXEL_BAD_ARGUMENT;
×
623
    }
624

625
    self = (sixel_loader_coregraphics_component_t *)component;
592✔
626
    if (self->has_bgcolor) {
592!
627
        bgcolor = self->bgcolor;
×
628
    }
×
629

630
    return load_with_coregraphics(chunk,
1,184✔
631
                                  self->fstatic,
592✔
632
                                  self->fuse_palette,
592✔
633
                                  self->reqcolors,
592✔
634
                                  bgcolor,
592✔
635
                                  self->loop_control,
592✔
636
                                  self->has_start_frame_no,
592✔
637
                                  self->start_frame_no,
592✔
638
                                  fn_load,
592✔
639
                                  context);
592✔
640
}
592✔
641

642
static char const *
643
sixel_loader_coregraphics_name(sixel_loader_component_t const *component)
584✔
644
{
645
    (void)component;
584✔
646
    return "coregraphics";
584✔
647
}
648

649
static sixel_loader_component_vtbl_t const g_sixel_loader_coregraphics_vtbl = {
650
    sixel_loader_coregraphics_ref,
651
    sixel_loader_coregraphics_unref,
652
    sixel_loader_coregraphics_setopt,
653
    sixel_loader_coregraphics_load,
654
    sixel_loader_coregraphics_name
655
};
656

657
SIXELSTATUS
658
sixel_loader_coregraphics_new(sixel_allocator_t *allocator,
7,554✔
659
                              sixel_loader_component_t **ppcomponent)
660
{
661
    sixel_loader_coregraphics_component_t *self;
4,667✔
662

663
    self = NULL;
7,554✔
664
    if (allocator == NULL || ppcomponent == NULL) {
7,554!
665
        return SIXEL_BAD_ARGUMENT;
×
666
    }
667

668
    *ppcomponent = NULL;
7,554✔
669
    self = (sixel_loader_coregraphics_component_t *)
7,554✔
670
        sixel_allocator_malloc(allocator, sizeof(*self));
7,554✔
671
    if (self == NULL) {
7,554✔
672
        return SIXEL_BAD_ALLOCATION;
×
673
    }
674

675
    memset(self, 0, sizeof(*self));
7,554✔
676
    self->base.vtbl = &g_sixel_loader_coregraphics_vtbl;
7,554✔
677
    self->allocator = allocator;
7,554✔
678
    self->ref = 1u;
7,554✔
679
    self->reqcolors = SIXEL_PALETTE_MAX;
7,554✔
680
    self->loop_control = SIXEL_LOOP_AUTO;
7,554✔
681
    self->start_frame_no = INT_MIN;
7,554✔
682
    sixel_allocator_ref(allocator);
7,554✔
683
    *ppcomponent = &self->base;
7,554✔
684
    return SIXEL_OK;
7,554✔
685
}
7,554✔
686

687
#endif  /* HAVE_COREGRAPHICS */
688

689
#if !HAVE_COREGRAPHICS
690
/*
691
 * Anchor a harmless symbol so the translation unit stays non-empty when
692
 * CoreGraphics is unavailable.
693
 */
694
typedef int loader_coregraphics_disabled;
695
#endif
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