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

saitoha / libsixel / 19918707358

04 Dec 2025 05:12AM UTC coverage: 38.402% (-4.0%) from 42.395%
19918707358

push

github

saitoha
tests: fix meson msys dll lookup

9738 of 38220 branches covered (25.48%)

12841 of 33438 relevant lines covered (38.4%)

782420.02 hits per line

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

27.33
/src/loader-quicklook.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
 * QuickLook thumbnail loader carved out of loader.c to keep macOS-specific
26
 * headers contained. The helper mirrors the previous flow: probe QuickLook,
27
 * request a thumbnail, and blit it into a sixel frame.
28
 */
29

30
#include "config.h"
31

32
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
33

34
#include <stdio.h>
35

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

40
#include <CoreServices/CoreServices.h>
41
#include <QuickLook/QuickLook.h>
42

43
#include <sixel.h>
44

45
#include "chunk.h"
46
#include "frame.h"
47
#include "loader-common.h"
48
#include "loader-quicklook.h"
49

50
#if HAVE_QUICKLOOK_THUMBNAILING
51
CGImageRef
52
sixel_quicklook_thumbnail_create(CFURLRef url, CGSize max_size);
53
#endif
54

55
int
56
loader_quicklook_can_decode(sixel_chunk_t const *pchunk,
4✔
57
                            char const *filename)
58
{
59
    char const *path;
4✔
60
    CFStringRef path_ref;
4✔
61
    CFURLRef url;
4✔
62
    CGFloat max_dimension;
4✔
63
    CGSize max_size;
4✔
64
    CGImageRef image;
4✔
65
    int result;
4✔
66
    int hint;
4✔
67

68
    path = NULL;
4✔
69
    path_ref = NULL;
4✔
70
    url = NULL;
4✔
71
    image = NULL;
4✔
72
    result = 0;
4✔
73

74
    loader_thumbnailer_initialize_size_hint();
4✔
75

76
    if (pchunk != NULL && pchunk->source_path != NULL) {
4!
77
        path = pchunk->source_path;
2✔
78
    } else if (filename != NULL) {
4!
79
        path = filename;
×
80
    }
×
81

82
    if (path == NULL || strcmp(path, "-") == 0 ||
4!
83
            strstr(path, "://") != NULL) {
2✔
84
        return 0;
2✔
85
    }
86

87
    path_ref = CFStringCreateWithCString(kCFAllocatorDefault,
4✔
88
                                         path,
2✔
89
                                         kCFStringEncodingUTF8);
90
    if (path_ref == NULL) {
2!
91
        return 0;
×
92
    }
93

94
    url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
4✔
95
                                        path_ref,
2✔
96
                                        kCFURLPOSIXPathStyle,
97
                                        false);
98
    CFRelease(path_ref);
2✔
99
    path_ref = NULL;
2✔
100
    if (url == NULL) {
2!
101
        return 0;
×
102
    }
103

104
    hint = loader_thumbnailer_get_size_hint();
2✔
105
    if (hint > 0) {
2!
106
        max_dimension = (CGFloat)hint;
2✔
107
    } else {
2✔
108
        max_dimension = (CGFloat)loader_thumbnailer_get_default_size_hint();
×
109
    }
110
    max_size.width = max_dimension;
2✔
111
    max_size.height = max_dimension;
2✔
112

113
#if HAVE_QUICKLOOK_THUMBNAILING
114
    image = sixel_quicklook_thumbnail_create(url, max_size);
2✔
115
    if (image == NULL) {
2!
116
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
117
#  pragma clang diagnostic push
118
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
119
# endif
120
        image = QLThumbnailImageCreate(kCFAllocatorDefault,
4✔
121
                                       url,
2✔
122
                                       max_size,
123
                                       NULL);
124
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
125
#  pragma clang diagnostic pop
126
# endif
127
    }
2✔
128
#else
129
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
130
#  pragma clang diagnostic push
131
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
132
# endif
133
    image = QLThumbnailImageCreate(kCFAllocatorDefault,
134
                                   url,
135
                                   max_size,
136
                                   NULL);
137
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
138
#  pragma clang diagnostic pop
139
# endif
140
#endif
141

142
    if (image != NULL) {
2!
143
        result = 1;
×
144
        CGImageRelease(image);
×
145
        image = NULL;
×
146
    }
×
147

148
    CFRelease(url);
2✔
149
    url = NULL;
2✔
150

151
    return result;
2✔
152
}
4✔
153

154
int
155
loader_quicklook_can_decode_chunk(sixel_chunk_t const *pchunk)
3✔
156
{
157
    /*
158
     * Registry predicates receive only the chunk. This wrapper forwards to the
159
     * full probe while omitting any filename hint so the registry table keeps
160
     * type-safe pointers.
161
     */
162
    return loader_quicklook_can_decode(pchunk, NULL);
3✔
163
}
164

165
SIXELSTATUS
166
load_with_quicklook(
×
167
    sixel_chunk_t const       /* in */     *pchunk,
168
    int                       /* in */     fstatic,
169
    int                       /* in */     fuse_palette,
170
    int                       /* in */     reqcolors,
171
    unsigned char             /* in */     *bgcolor,
172
    int                       /* in */     loop_control,
173
    sixel_load_image_function /* in */     fn_load,
174
    void                      /* in/out */ *context)
175
{
176
    SIXELSTATUS status = SIXEL_FALSE;
×
177
    sixel_frame_t *frame = NULL;
×
178
    CFStringRef path = NULL;
×
179
    CFURLRef url = NULL;
×
180
    CGImageRef image = NULL;
×
181
    CGColorSpaceRef color_space = NULL;
×
182
    CGContextRef ctx = NULL;
×
183
    CGRect bounds;
×
184
    size_t stride;
×
185
    unsigned char fill_color[3];
×
186
    CGFloat fill_r;
×
187
    CGFloat fill_g;
×
188
    CGFloat fill_b;
×
189
    CGFloat max_dimension;
×
190
    CGSize max_size;
×
191
    unsigned char *pixels;
×
192
    int hint;
×
193

194
    (void)fstatic;
×
195
    (void)fuse_palette;
×
196
    (void)reqcolors;
×
197
    (void)loop_control;
×
198

199
    if (pchunk == NULL || pchunk->source_path == NULL) {
×
200
        goto end;
×
201
    }
202

203
    loader_thumbnailer_initialize_size_hint();
×
204

205
    status = sixel_frame_new(&frame, pchunk->allocator);
×
206
    if (SIXEL_FAILED(status)) {
×
207
        goto end;
×
208
    }
209

210
    path = CFStringCreateWithCString(kCFAllocatorDefault,
×
211
                                     pchunk->source_path,
×
212
                                     kCFStringEncodingUTF8);
213
    if (path == NULL) {
×
214
        status = SIXEL_RUNTIME_ERROR;
×
215
        goto end;
×
216
    }
217

218
    url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
×
219
                                        path,
×
220
                                        kCFURLPOSIXPathStyle,
221
                                        false);
222
    if (url == NULL) {
×
223
        status = SIXEL_RUNTIME_ERROR;
×
224
        goto end;
×
225
    }
226

227
    hint = loader_thumbnailer_get_size_hint();
×
228
    if (hint > 0) {
×
229
        max_dimension = (CGFloat)hint;
×
230
    } else {
×
231
        max_dimension = (CGFloat)loader_thumbnailer_get_default_size_hint();
×
232
    }
233
    max_size.width = max_dimension;
×
234
    max_size.height = max_dimension;
×
235

236
#if HAVE_QUICKLOOK_THUMBNAILING
237
    image = sixel_quicklook_thumbnail_create(url, max_size);
×
238
    if (image == NULL) {
×
239
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
240
#  pragma clang diagnostic push
241
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
242
# endif
243
        image = QLThumbnailImageCreate(kCFAllocatorDefault,
×
244
                                       url,
×
245
                                       max_size,
246
                                       NULL);
247
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
248
#  pragma clang diagnostic pop
249
# endif
250
    }
×
251
#else
252
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
253
#  pragma clang diagnostic push
254
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
255
# endif
256
    image = QLThumbnailImageCreate(kCFAllocatorDefault,
257
                                   url,
258
                                   max_size,
259
                                   NULL);
260
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
261
#  pragma clang diagnostic pop
262
# endif
263
#endif
264

265
    if (image == NULL) {
×
266
        status = SIXEL_RUNTIME_ERROR;
×
267
        goto end;
×
268
    }
269

270
    bounds = CGRectMake(0.0, 0.0,
×
271
                        (CGFloat)CGImageGetWidth(image),
×
272
                        (CGFloat)CGImageGetHeight(image));
×
273
    frame->width = (int)bounds.size.width;
×
274
    frame->height = (int)bounds.size.height;
×
275
    frame->pixelformat = SIXEL_PIXELFORMAT_RGB888;
×
276
    stride = (size_t)frame->width * 4;
×
277
    fill_r = (CGFloat)bgcolor[0] / 255.0;
×
278
    fill_g = (CGFloat)bgcolor[1] / 255.0;
×
279
    fill_b = (CGFloat)bgcolor[2] / 255.0;
×
280
    fill_color[0] = bgcolor[0];
×
281
    fill_color[1] = bgcolor[1];
×
282
    fill_color[2] = bgcolor[2];
×
283
    /* QuickLook renders into RGB888 so no palette mapping is required. */
284
    sixel_frame_set_pixels(frame,
×
285
                           sixel_allocator_malloc(
×
286
                               pchunk->allocator,
×
287
                               (size_t)(frame->height * stride)));
×
288
    pixels = sixel_frame_get_pixels(frame);
×
289
    if (pixels == NULL) {
×
290
        status = SIXEL_BAD_ALLOCATION;
×
291
        goto end;
×
292
    }
293

294
    color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
×
295
    if (color_space == NULL) {
×
296
        status = SIXEL_RUNTIME_ERROR;
×
297
        goto end;
×
298
    }
299
    ctx = CGBitmapContextCreate(pixels,
×
300
                                (size_t)frame->width,
×
301
                                (size_t)frame->height,
×
302
                                8,
303
                                stride,
×
304
                                color_space,
×
305
                                kCGImageAlphaPremultipliedLast |
306
                                        kCGBitmapByteOrder32Big);
307
    if (ctx == NULL) {
×
308
        status = SIXEL_RUNTIME_ERROR;
×
309
        goto end;
×
310
    }
311

312
    CGContextSetRGBFillColor(ctx, fill_r, fill_g, fill_b, 1.0);
×
313
    CGContextFillRect(ctx, bounds);
×
314
    CGContextDrawImage(ctx, bounds, image);
×
315
    frame->multiframe = 0;
×
316
    frame->frame_no = 0;
×
317
    frame->delay = 0;
×
318
    status = fn_load(frame, context);
×
319
    if (status != SIXEL_OK) {
×
320
        goto end;
×
321
    }
322
    status = SIXEL_OK;
×
323

324
end:
325
    if (ctx) {
×
326
        CGContextRelease(ctx);
×
327
    }
×
328
    if (color_space) {
×
329
        CGColorSpaceRelease(color_space);
×
330
    }
×
331
    if (image) {
×
332
        CGImageRelease(image);
×
333
    }
×
334
    if (url) {
×
335
        CFRelease(url);
×
336
    }
×
337
    if (path) {
×
338
        CFRelease(path);
×
339
    }
×
340
    if (frame) {
×
341
        sixel_frame_unref(frame);
×
342
    }
×
343
    return status;
×
344
}
×
345

346
#endif  /* HAVE_COREGRAPHICS && HAVE_QUICKLOOK */
347

348
#if !(HAVE_COREGRAPHICS && HAVE_QUICKLOOK)
349
/*
350
 * Preserve a non-empty unit when QuickLook support is disabled to avoid
351
 * pedantic compiler warnings.
352
 */
353
typedef int loader_quicklook_disabled;
354
#endif
355

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