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

saitoha / libsixel / 20114228509

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

push

github

saitoha
tests: temporarily skip oversized python error scenario

11613 of 40017 branches covered (29.02%)

19771 of 38321 relevant lines covered (51.59%)

4030622.38 hits per line

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

26.52
/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
#include "config.h"
31

32
#if HAVE_COREGRAPHICS
33

34
#include <stdio.h>
35

36
#if HAVE_STRING_H
37
# include <string.h>
38
#endif
39

40
#include <ApplicationServices/ApplicationServices.h>
41
#include <ImageIO/ImageIO.h>
42

43
#include <sixel.h>
44

45
#include "chunk.h"
46
#include "frame.h"
47
#include "loader-coregraphics.h"
48
#include "logger.h"
49

50
SIXELSTATUS
51
load_with_coregraphics(
7✔
52
    sixel_chunk_t const       /* in */     *pchunk,
53
    int                       /* in */     fstatic,
54
    int                       /* in */     fuse_palette,
55
    int                       /* in */     reqcolors,
56
    unsigned char             /* in */     *bgcolor,
57
    int                       /* in */     loop_control,
58
    sixel_load_image_function /* in */     fn_load,
59
    void                      /* in/out */ *context)
60
{
61
    SIXELSTATUS status = SIXEL_FALSE;
7✔
62
    sixel_frame_t *frame = NULL;
7✔
63
    CFDataRef data = NULL;
7✔
64
    CGImageSourceRef source = NULL;
7✔
65
    CGImageRef image = NULL;
7✔
66
    CGColorSpaceRef color_space = NULL;
7✔
67
    CGContextRef ctx = NULL;
7✔
68
    size_t stride;
7✔
69
    size_t frame_count;
7✔
70
    int anim_loop_count = (-1);
7✔
71
    CFDictionaryRef props = NULL;
7✔
72
    CFDictionaryRef anim_dict;
7✔
73
    CFNumberRef loop_num;
7✔
74
    CFDictionaryRef frame_props;
7✔
75
    CFDictionaryRef frame_anim_dict;
7✔
76
    CFNumberRef delay_num;
7✔
77
    double delay_sec;
7✔
78
    unsigned char *pixels;
7✔
79

80
    (void) fuse_palette;
7✔
81
    (void) reqcolors;
7✔
82
    (void) bgcolor;
7✔
83

84
    status = sixel_frame_new(&frame, pchunk->allocator);
7✔
85
    if (SIXEL_FAILED(status)) {
7!
86
        goto end;
×
87
    }
88

89
    data = CFDataCreate(kCFAllocatorDefault,
14✔
90
                        pchunk->buffer,
7✔
91
                        (CFIndex)pchunk->size);
7✔
92
    if (! data) {
7!
93
        status = SIXEL_FALSE;
×
94
        goto end;
×
95
    }
96

97
    source = CGImageSourceCreateWithData(data, NULL);
7✔
98
    if (! source) {
7!
99
        status = SIXEL_FALSE;
×
100
        goto end;
×
101
    }
102

103
    frame_count = CGImageSourceGetCount(source);
7✔
104
    if (! frame_count) {
7!
105
        status = SIXEL_FALSE;
7✔
106
        goto end;
7✔
107
    }
108
    if (fstatic) {
×
109
        frame_count = 1;
×
110
    }
×
111

112
    props = CGImageSourceCopyProperties(source, NULL);
×
113
    if (props) {
×
114
        anim_dict = (CFDictionaryRef)CFDictionaryGetValue(
×
115
            props, kCGImagePropertyGIFDictionary);
×
116
        if (anim_dict) {
×
117
            loop_num = (CFNumberRef)CFDictionaryGetValue(
×
118
                anim_dict, kCGImagePropertyGIFLoopCount);
×
119
            if (loop_num) {
×
120
                CFNumberGetValue(loop_num, kCFNumberIntType, &anim_loop_count);
×
121
            }
×
122
        }
×
123
    }
×
124

125
    frame->frame_no = 0;
×
126
    frame->loop_count = 0;
×
127
    frame->multiframe = (frame_count > 1);
×
128
    for (;;) {
×
129
        image = CGImageSourceCreateImageAtIndex(
×
130
            source, (unsigned int)frame->frame_no, NULL);
×
131
        if (! image) {
×
132
            status = SIXEL_FALSE;
×
133
            goto end;
×
134
        }
135

136
        frame_props = CGImageSourceCopyPropertiesAtIndex(
×
137
            source, (unsigned int)frame->frame_no, NULL);
×
138
        if (frame_props) {
×
139
            frame_anim_dict = (CFDictionaryRef)CFDictionaryGetValue(
×
140
                frame_props, kCGImagePropertyGIFDictionary);
×
141
            if (frame_anim_dict) {
×
142
                loop_num = (CFNumberRef)CFDictionaryGetValue(
×
143
                    frame_anim_dict, kCGImagePropertyGIFLoopCount);
×
144
                if (loop_num) {
×
145
                    CFNumberGetValue(loop_num,
×
146
                                     kCFNumberIntType,
147
                                     &anim_loop_count);
148
                }
×
149
                delay_num = (CFNumberRef)CFDictionaryGetValue(
×
150
                    frame_anim_dict, kCGImagePropertyGIFUnclampedDelayTime);
×
151
                if (! delay_num) {
×
152
                    delay_num = (CFNumberRef)CFDictionaryGetValue(
×
153
                        frame_anim_dict, kCGImagePropertyGIFDelayTime);
×
154
                }
×
155
                if (delay_num) {
×
156
                    CFNumberGetValue(delay_num, kCFNumberDoubleType, &delay_sec);
×
157
                    if (delay_sec < 0) {
×
158
                        delay_sec = 0.0;
×
159
                    }
×
160
                    frame->delay = (int)(delay_sec * 100);
×
161
                }
×
162
            }
×
163
            CFRelease(frame_props);
×
164
            frame_props = NULL;
×
165
        }
×
166

167
        frame->width = (int)CGImageGetWidth(image);
×
168
        frame->height = (int)CGImageGetHeight(image);
×
169
        frame->pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
170

171
        if (frame->width > SIXEL_WIDTH_LIMIT) {
×
172
            sixel_helper_set_additional_message(
×
173
                "load_with_coregraphics: given width parameter is too huge.");
174
            status = SIXEL_BAD_INPUT;
×
175
            goto end;
×
176
        }
177
        if (frame->height > SIXEL_HEIGHT_LIMIT) {
×
178
            sixel_helper_set_additional_message(
×
179
                "load_with_coregraphics: given height parameter is too huge.");
180
            status = SIXEL_BAD_INPUT;
×
181
            goto end;
×
182
        }
183
        if (frame->width <= 0) {
×
184
            sixel_helper_set_additional_message(
×
185
                "load_with_coregraphics: an invalid width parameter detected.");
186
            status = SIXEL_BAD_INPUT;
×
187
            goto end;
×
188
        }
189
        if (frame->height <= 0) {
×
190
            sixel_helper_set_additional_message(
×
191
                "load_with_coregraphics: an invalid width parameter detected.");
192
            status = SIXEL_BAD_INPUT;
×
193
            goto end;
×
194
        }
195
        if (frame->height >= INT_MAX / 4 || frame->width >= INT_MAX / 4 ||
×
196
                frame->height * frame->width * 4 >= INT_MAX) {
×
197
            sixel_helper_set_additional_message(
×
198
                "load_with_coregraphics: too large image.");
199
            status = SIXEL_RUNTIME_ERROR;
×
200
            goto end;
×
201
        }
202

203
        stride = (size_t)frame->width * 4;
×
204
        sixel_frame_set_pixels(frame,
×
205
                               sixel_allocator_malloc(
×
206
                                   pchunk->allocator,
×
207
                                   (size_t)(frame->height * stride)));
×
208
        pixels = sixel_frame_get_pixels(frame);
×
209
        if (pixels == NULL) {
×
210
            sixel_helper_set_additional_message(
×
211
                "load_with_coregraphics: sixel_allocator_malloc() failed.");
212
            status = SIXEL_BAD_ALLOCATION;
×
213
            goto end;
×
214
        }
215

216
        color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
×
217
        if (! color_space) {
×
218
            CGImageRelease(image);
×
219
            goto end;
×
220
        }
221

222
        ctx = CGBitmapContextCreate(pixels,
×
223
                                    (size_t)frame->width,
×
224
                                    (size_t)frame->height,
×
225
                                    8,
226
                                    stride,
×
227
                                    color_space,
×
228
                                    kCGImageAlphaPremultipliedLast |
229
                                            kCGBitmapByteOrder32Big);
230
        if (!ctx) {
×
231
            CGImageRelease(image);
×
232
            goto end;
×
233
        }
234

235
        CGContextDrawImage(ctx,
×
236
                           CGRectMake(0, 0, frame->width, frame->height),
×
237
                           image);
×
238
        CGContextRelease(ctx);
×
239
        ctx = NULL;
×
240

241
        frame->multiframe = (frame_count > 1);
×
242
        status = fn_load(frame, context);
×
243
        CGImageRelease(image);
×
244
        image = NULL;
×
245
        if (status != SIXEL_OK) {
×
246
            goto end;
×
247
        }
248
        ++frame->frame_no;
×
249

250
        ++frame->loop_count;
×
251

252
        if (frame_count <= 1) {
×
253
            break;
×
254
        }
255
        if (loop_control == SIXEL_LOOP_DISABLE) {
×
256
            break;
×
257
        }
258
        if (loop_control == SIXEL_LOOP_AUTO) {
×
259
            if (anim_loop_count < 0) {
×
260
                break;
×
261
            }
262
            if (anim_loop_count > 0 && frame->loop_count >= anim_loop_count) {
×
263
                break;
×
264
            }
265
            continue;
×
266
        }
267
    }
268

269
    status = SIXEL_OK;
×
270

271
end:
272
    if (ctx) {
7!
273
        CGContextRelease(ctx);
×
274
    }
×
275
    if (color_space) {
7!
276
        CGColorSpaceRelease(color_space);
×
277
    }
×
278
    if (image) {
7!
279
        CGImageRelease(image);
×
280
    }
×
281
    if (source) {
7!
282
        CFRelease(source);
7✔
283
    }
7✔
284
    if (data) {
7!
285
        CFRelease(data);
7✔
286
    }
7✔
287
    if (frame) {
7!
288
        sixel_frame_unref(frame);
7✔
289
    }
7✔
290
    return status;
14✔
291
}
7✔
292

293
#endif  /* HAVE_COREGRAPHICS */
294

295
#if !HAVE_COREGRAPHICS
296
/*
297
 * Anchor a harmless symbol so the translation unit stays non-empty when
298
 * CoreGraphics is unavailable.
299
 */
300
typedef int loader_coregraphics_disabled;
301
#endif
302

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