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

saitoha / libsixel / 25651620612

11 May 2026 04:12AM UTC coverage: 86.747% (-0.002%) from 86.749%
25651620612

push

github

saitoha
fix: gate img2sixel AFL unsetenv API

127374 of 257244 branches covered (49.51%)

153078 of 176465 relevant lines covered (86.75%)

9509470.93 hits per line

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

90.5
/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
#if defined(HAVE_CONFIG_H)
31
#include "config.h"
32
#endif
33

34
#if HAVE_COREGRAPHICS && HAVE_QUICKLOOK
35

36
#include <stdio.h>
37

38
#if HAVE_STRING_H
39
# include <string.h>
40
#endif
41

42
#include <CoreServices/CoreServices.h>
43
#include <QuickLook/QuickLook.h>
44

45
#include <sixel.h>
46

47
#include "chunk-view.h"
48
#include "frame-private.h"
49
#include "frame-factory.h"
50
#include "loader-common.h"
51
#include "loader-quicklook.h"
52

53
typedef struct sixel_loader_quicklook_component {
54
    sixel_loader_component_t base;
55
    sixel_allocator_t *allocator;
56
    unsigned int ref;
57
    int fstatic;
58
    int fuse_palette;
59
    int reqcolors;
60
    unsigned char bgcolor[3];
61
    int has_bgcolor;
62
    int loop_control;
63
    int has_start_frame_no;
64
    int start_frame_no;
65
} sixel_loader_quicklook_component_t;
66

67
static unsigned char
68
quicklook_unpremultiply_channel(unsigned int value, unsigned int alpha)
341,437,440✔
69
{
70
    unsigned int unpremultiplied;
213,398,400✔
71

72
    if (alpha == 0u) {
341,437,440✔
73
        return 0u;
1,769,472✔
74
    }
75
    if (alpha >= 255u) {
339,667,968✔
76
        return (unsigned char)value;
333,769,728✔
77
    }
78

79
    unpremultiplied = (value * 255u + alpha / 2u) / alpha;
5,898,240✔
80
    if (unpremultiplied > 255u) {
5,898,240!
81
        unpremultiplied = 255u;
×
82
    }
×
83
    return (unsigned char)unpremultiplied;
5,898,240✔
84
}
341,437,440✔
85

86
static void
87
quicklook_finalize_frame_pixels(sixel_frame_t *frame,
840✔
88
                                unsigned char const *bgcolor)
89
{
90
    unsigned char *pixels;
525✔
91
    size_t pixel_total;
525✔
92
    size_t pixel_index;
525✔
93
    unsigned int alpha;
525✔
94
    int inspect_alpha;
525✔
95
    int preserve_alpha;
525✔
96

97
    pixels = NULL;
840✔
98
    pixel_total = 0u;
840✔
99
    pixel_index = 0u;
840✔
100
    alpha = 0u;
840✔
101
    inspect_alpha = 0;
840✔
102
    preserve_alpha = 0;
840✔
103
    if (frame == NULL) {
840✔
104
        return;
×
105
    }
106

107
    pixels = sixel_frame_get_pixels(frame);
840✔
108
    if (pixels == NULL || frame->width <= 0 || frame->height <= 0) {
840!
109
        return;
×
110
    }
111

112
    pixel_total = (size_t)frame->width * (size_t)frame->height;
840✔
113
    inspect_alpha = bgcolor == NULL ? 1 : 0;
840✔
114
    if (inspect_alpha) {
840✔
115
        for (pixel_index = 0u; pixel_index < pixel_total; ++pixel_index) {
113,813,312✔
116
            alpha = pixels[pixel_index * 4u + 3u];
113,812,480✔
117
            if (alpha != 255u) {
113,812,480✔
118
                preserve_alpha = 1;
2,555,904✔
119
            }
2,555,904✔
120
            pixels[pixel_index * 4u + 0u] =
113,812,480✔
121
                quicklook_unpremultiply_channel(
113,812,480✔
122
                    pixels[pixel_index * 4u + 0u], alpha);
113,812,480✔
123
            pixels[pixel_index * 4u + 1u] =
113,812,480✔
124
                quicklook_unpremultiply_channel(
113,812,480✔
125
                    pixels[pixel_index * 4u + 1u], alpha);
113,812,480✔
126
            pixels[pixel_index * 4u + 2u] =
113,812,480✔
127
                quicklook_unpremultiply_channel(
113,812,480✔
128
                    pixels[pixel_index * 4u + 2u], alpha);
113,812,480✔
129
        }
113,812,480✔
130
    }
832✔
131

132
    if (!preserve_alpha) {
842✔
133
        for (pixel_index = 0u; pixel_index < pixel_total; ++pixel_index) {
111,650,600✔
134
            pixels[pixel_index * 3u + 0u] = pixels[pixel_index * 4u + 0u];
111,649,792✔
135
            pixels[pixel_index * 3u + 1u] = pixels[pixel_index * 4u + 1u];
111,649,792✔
136
            pixels[pixel_index * 3u + 2u] = pixels[pixel_index * 4u + 2u];
111,649,792✔
137
        }
111,649,792✔
138
    }
808✔
139

140
    frame->pixelformat = preserve_alpha
840✔
141
        ? SIXEL_PIXELFORMAT_RGBA8888
142
        : SIXEL_PIXELFORMAT_RGB888;
143
    frame->colorspace = SIXEL_COLORSPACE_GAMMA;
840✔
144
    frame->transparent = -1;
840✔
145
    frame->alpha_zero_is_transparent = preserve_alpha ? 1 : 0;
840✔
146
}
840!
147

148
static void
149
quicklook_set_error_message(char const *context)
48✔
150
{
151
    if (context == NULL) {
48✔
152
        return;
×
153
    }
154
    sixel_helper_set_additional_message(context);
48✔
155
}
48✔
156

157
#if HAVE_QUICKLOOK_THUMBNAILING
158
CGImageRef
159
sixel_quicklook_thumbnail_create(CFURLRef url, CGSize max_size);
160
#endif
161

162
static int
163
loader_quicklook_can_decode(sixel_chunk_t const *pchunk,
472✔
164
                            char const *filename)
165
{
166
    char const *path;
295✔
167
    CFStringRef path_ref;
295✔
168
    CFURLRef url;
295✔
169
    CGFloat max_dimension;
295✔
170
    CGSize max_size;
295✔
171
    CGImageRef image;
295✔
172
    int result;
295✔
173
    int hint;
295✔
174

175
    path = NULL;
472✔
176
    path_ref = NULL;
472✔
177
    url = NULL;
472✔
178
    image = NULL;
472✔
179
    result = 0;
472✔
180

181
    loader_thumbnailer_initialize_size_hint();
472✔
182

183
    if (pchunk != NULL && sixel_chunk_get_source_path(pchunk) != NULL) {
472!
184
        path = sixel_chunk_get_source_path(pchunk);
456✔
185
    } else if (filename != NULL) {
470!
186
        path = filename;
×
187
    }
×
188

189
    if (path == NULL || strcmp(path, "-") == 0 ||
472!
190
            strstr(path, "://") != NULL) {
456✔
191
        return 0;
16✔
192
    }
193

194
    path_ref = CFStringCreateWithCString(kCFAllocatorDefault,
912✔
195
                                         path,
456✔
196
                                         kCFStringEncodingUTF8);
197
    if (path_ref == NULL) {
456✔
198
        return 0;
×
199
    }
200

201
    url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
912✔
202
                                        path_ref,
456✔
203
                                        kCFURLPOSIXPathStyle,
204
                                        false);
205
    CFRelease(path_ref);
456✔
206
    path_ref = NULL;
456✔
207
    if (url == NULL) {
456✔
208
        return 0;
×
209
    }
210

211
    hint = loader_thumbnailer_get_size_hint();
456✔
212
    if (hint > 0) {
456!
213
        max_dimension = (CGFloat)hint;
456✔
214
    } else {
456✔
215
        max_dimension = (CGFloat)loader_thumbnailer_get_default_size_hint();
×
216
    }
217
    max_size.width = max_dimension;
456✔
218
    max_size.height = max_dimension;
456✔
219

220
#if HAVE_QUICKLOOK_THUMBNAILING
221
    image = sixel_quicklook_thumbnail_create(url, max_size);
456✔
222
    if (image == NULL) {
456✔
223
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
224
#  pragma clang diagnostic push
225
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
226
# endif
227
        image = QLThumbnailImageCreate(kCFAllocatorDefault,
80✔
228
                                       url,
40✔
229
                                       max_size,
230
                                       NULL);
231
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
232
#  pragma clang diagnostic pop
233
# endif
234
    }
40✔
235
#else
236
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
237
#  pragma clang diagnostic push
238
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
239
# endif
240
    image = QLThumbnailImageCreate(kCFAllocatorDefault,
241
                                   url,
242
                                   max_size,
243
                                   NULL);
244
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
245
#  pragma clang diagnostic pop
246
# endif
247
#endif
248

249
    if (image != NULL) {
451✔
250
        result = 1;
416✔
251
        CGImageRelease(image);
416✔
252
        image = NULL;
416✔
253
    }
416✔
254

255
    CFRelease(url);
456✔
256
    url = NULL;
456✔
257

258
    return result;
456✔
259
}
472✔
260

261
static int
262
loader_quicklook_can_decode_chunk(sixel_chunk_t const *pchunk)
472✔
263
{
264
    /*
265
     * Registry predicates receive only the chunk. This wrapper forwards to the
266
     * full probe while omitting any filename hint so the registry table keeps
267
     * type-safe pointers.
268
     */
269
    return loader_quicklook_can_decode(pchunk, NULL);
472✔
270
}
271

272
static SIXELSTATUS
273
load_with_quicklook(
888✔
274
    sixel_chunk_t const       /* in */     *pchunk,
275
    sixel_allocator_t         /* in */     *allocator,
276
    int                       /* in */     fstatic,
277
    int                       /* in */     fuse_palette,
278
    int                       /* in */     reqcolors,
279
    unsigned char             /* in */     *bgcolor,
280
    int                       /* in */     loop_control,
281
    int                       /* in */     start_frame_no_set,
282
    int                       /* in */     start_frame_no,
283
    sixel_load_image_function /* in */     fn_load,
284
    void                      /* in/out */ *context)
285
{
286
    SIXELSTATUS status = SIXEL_FALSE;
888✔
287
    sixel_frame_t *frame = NULL;
888✔
288
    CFStringRef path = NULL;
888✔
289
    CFURLRef url = NULL;
888✔
290
    CGImageRef image = NULL;
888✔
291
    CGColorSpaceRef color_space = NULL;
888✔
292
    CGContextRef ctx = NULL;
888✔
293
    CGRect bounds;
555✔
294
    size_t stride;
555✔
295
    CGFloat fill_r;
555✔
296
    CGFloat fill_g;
555✔
297
    CGFloat fill_b;
555✔
298
    CGFloat max_dimension;
555✔
299
    CGSize max_size;
555✔
300
    unsigned char *pixels;
555✔
301
    int hint;
555✔
302

303
    (void)fstatic;
888✔
304
    (void)fuse_palette;
888✔
305
    (void)reqcolors;
888✔
306
    (void)loop_control;
888✔
307
    (void)start_frame_no_set;
888✔
308
    (void)start_frame_no;
888✔
309

310
    if (pchunk == NULL || allocator == NULL || fn_load == NULL ||
888!
311
        sixel_chunk_get_source_path(pchunk) == NULL) {
888✔
312
        quicklook_set_error_message(
×
313
            "load_with_quicklook: source path is unavailable.");
314
        goto end;
×
315
    }
316

317
    loader_thumbnailer_initialize_size_hint();
888✔
318

319
    status = sixel_frame_create_from_factory(&frame, allocator);
888✔
320
    if (SIXEL_FAILED(status)) {
888!
321
        goto end;
×
322
    }
323

324
    path = CFStringCreateWithCString(kCFAllocatorDefault,
1,776✔
325
                                     sixel_chunk_get_source_path(pchunk),
888✔
326
                                     kCFStringEncodingUTF8);
327
    if (path == NULL) {
888✔
328
        quicklook_set_error_message(
×
329
            "load_with_quicklook: CFStringCreateWithCString failed.");
330
        status = SIXEL_RUNTIME_ERROR;
×
331
        goto end;
×
332
    }
333

334
    url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
1,776✔
335
                                        path,
888✔
336
                                        kCFURLPOSIXPathStyle,
337
                                        false);
338
    if (url == NULL) {
888✔
339
        quicklook_set_error_message(
×
340
            "load_with_quicklook: CFURLCreateWithFileSystemPath failed.");
341
        status = SIXEL_RUNTIME_ERROR;
×
342
        goto end;
×
343
    }
344

345
    hint = loader_thumbnailer_get_size_hint();
888✔
346
    if (hint > 0) {
888!
347
        max_dimension = (CGFloat)hint;
888✔
348
    } else {
888✔
349
        max_dimension = (CGFloat)loader_thumbnailer_get_default_size_hint();
×
350
    }
351
    max_size.width = max_dimension;
888✔
352
    max_size.height = max_dimension;
888✔
353

354
#if HAVE_QUICKLOOK_THUMBNAILING
355
    image = sixel_quicklook_thumbnail_create(url, max_size);
888✔
356
    if (image == NULL) {
888✔
357
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
358
#  pragma clang diagnostic push
359
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
360
# endif
361
        image = QLThumbnailImageCreate(kCFAllocatorDefault,
160✔
362
                                       url,
80✔
363
                                       max_size,
364
                                       NULL);
365
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
366
#  pragma clang diagnostic pop
367
# endif
368
    }
80✔
369
#else
370
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
371
#  pragma clang diagnostic push
372
#  pragma clang diagnostic ignored "-Wdeprecated-declarations"
373
# endif
374
    image = QLThumbnailImageCreate(kCFAllocatorDefault,
375
                                   url,
376
                                   max_size,
377
                                   NULL);
378
# if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
379
#  pragma clang diagnostic pop
380
# endif
381
#endif
382

383
    if (image == NULL) {
888✔
384
        quicklook_set_error_message(
48✔
385
            "load_with_quicklook: QuickLook thumbnail creation failed.");
386
        status = SIXEL_RUNTIME_ERROR;
48✔
387
        goto end;
48✔
388
    }
389

390
    bounds = CGRectMake(0.0, 0.0,
1,365✔
391
                        (CGFloat)CGImageGetWidth(image),
840✔
392
                        (CGFloat)CGImageGetHeight(image));
840✔
393
    frame->width = (int)bounds.size.width;
840✔
394
    frame->height = (int)bounds.size.height;
840✔
395
    frame->pixelformat = SIXEL_PIXELFORMAT_RGBA8888;
840✔
396
    frame->colorspace = SIXEL_COLORSPACE_GAMMA;
840✔
397
    /*
398
     * QuickLook renders into a premultiplied RGBA buffer. Keep a four-byte
399
     * stride so the pixel format matches the bitmap context layout.
400
     */
401
    stride = (size_t)frame->width * 4;
840✔
402

403
    pixels = (unsigned char *)sixel_allocator_malloc(
840✔
404
        allocator,
840✔
405
        (size_t)(frame->height * stride));
840✔
406
    if (pixels == NULL) {
840✔
407
        quicklook_set_error_message(
×
408
            "load_with_quicklook: sixel_allocator_malloc() failed.");
409
        status = SIXEL_BAD_ALLOCATION;
×
410
        goto end;
×
411
    }
412
    status = sixel_frame_as_interface(frame)->vtbl->init_pixels(
1,680✔
413
        sixel_frame_as_interface(frame),
840✔
414
        &(sixel_frame_pixels_request_t){
5,040✔
415
            pixels,
840✔
416
            NULL,
417
            frame->width,
840✔
418
            frame->height,
840✔
419
            frame->pixelformat,
840✔
420
            frame->colorspace,
840✔
421
            -1,
422
            SIXEL_FRAME_PIXELS_U8
423
        });
424
    if (SIXEL_FAILED(status)) {
840!
425
        sixel_allocator_free(allocator, pixels);
×
426
        goto end;
×
427
    }
428

429
    color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
840✔
430
    if (color_space == NULL) {
840✔
431
        quicklook_set_error_message(
×
432
            "load_with_quicklook: CGColorSpaceCreateWithName failed.");
433
        status = SIXEL_RUNTIME_ERROR;
×
434
        goto end;
×
435
    }
436
    ctx = CGBitmapContextCreate(pixels,
1,680✔
437
                                (size_t)frame->width,
840✔
438
                                (size_t)frame->height,
840✔
439
                                8,
440
                                stride,
840✔
441
                                color_space,
840✔
442
                                kCGImageAlphaPremultipliedLast |
443
                                        kCGBitmapByteOrder32Big);
444
    if (ctx == NULL) {
840✔
445
        quicklook_set_error_message(
×
446
            "load_with_quicklook: CGBitmapContextCreate failed.");
447
        status = SIXEL_RUNTIME_ERROR;
×
448
        goto end;
×
449
    }
450

451
    if (bgcolor != NULL) {
840✔
452
        fill_r = (CGFloat)bgcolor[0] / 255.0;
8✔
453
        fill_g = (CGFloat)bgcolor[1] / 255.0;
8✔
454
        fill_b = (CGFloat)bgcolor[2] / 255.0;
8✔
455
        CGContextSetRGBFillColor(ctx, fill_r, fill_g, fill_b, 1.0);
8✔
456
        CGContextFillRect(ctx, bounds);
8✔
457
    } else {
8✔
458
        CGContextSetBlendMode(ctx, kCGBlendModeCopy);
832✔
459
        CGContextSetRGBFillColor(ctx, 0.0, 0.0, 0.0, 0.0);
832✔
460
        CGContextFillRect(ctx, bounds);
832✔
461
        CGContextSetBlendMode(ctx, kCGBlendModeNormal);
832✔
462
    }
463
    CGContextDrawImage(ctx, bounds, image);
840✔
464
    quicklook_finalize_frame_pixels(frame, bgcolor);
840✔
465
    frame->multiframe = 0;
840✔
466
    frame->frame_no = 0;
840✔
467
    frame->delay = 0;
840✔
468
    status = fn_load(frame, context);
840✔
469
    if (status != SIXEL_OK) {
840✔
470
        goto end;
416✔
471
    }
472
    status = SIXEL_OK;
424✔
473

474
end:
475
    if (ctx) {
888✔
476
        CGContextRelease(ctx);
840✔
477
    }
840✔
478
    if (color_space) {
882✔
479
        CGColorSpaceRelease(color_space);
840✔
480
    }
840✔
481
    if (image) {
882✔
482
        CGImageRelease(image);
840✔
483
    }
840✔
484
    if (url) {
888!
485
        CFRelease(url);
888✔
486
    }
888✔
487
    if (path) {
888!
488
        CFRelease(path);
888✔
489
    }
888✔
490
    if (frame) {
888!
491
        sixel_frame_unref(frame);
888✔
492
    }
888✔
493
    return status;
1,443✔
494
}
555✔
495

496

497
static void
498
sixel_loader_quicklook_ref(sixel_loader_component_t *component)
24,795✔
499
{
500
    sixel_loader_quicklook_component_t *self;
15,632✔
501

502
    self = NULL;
24,795✔
503
    if (component == NULL) {
24,795✔
504
        return;
×
505
    }
506

507
    self = (sixel_loader_quicklook_component_t *)component;
24,795✔
508
    ++self->ref;
24,795✔
509
}
24,795!
510

511
static void
512
sixel_loader_quicklook_unref(sixel_loader_component_t *component)
49,598✔
513
{
514
    sixel_loader_quicklook_component_t *self;
31,269✔
515
    sixel_allocator_t *allocator;
31,269✔
516

517
    self = NULL;
49,598✔
518
    allocator = NULL;
49,598✔
519
    if (component == NULL) {
49,598✔
520
        return;
×
521
    }
522

523
    self = (sixel_loader_quicklook_component_t *)component;
49,598✔
524
    if (self->ref == 0u) {
49,598✔
525
        return;
×
526
    }
527

528
    --self->ref;
49,598✔
529
    if (self->ref > 0u) {
49,598✔
530
        return;
24,795✔
531
    }
532

533
    allocator = self->allocator;
24,803✔
534
    sixel_allocator_free(allocator, self);
24,803✔
535
    sixel_allocator_unref(allocator);
24,803✔
536
}
49,598!
537

538
static SIXELSTATUS
539
sixel_loader_quicklook_setopt(sixel_loader_component_t *component,
471,137✔
540
                              int option,
541
                              void const *value)
542
{
543
    sixel_loader_quicklook_component_t *self;
297,028✔
544
    int const *flag;
297,028✔
545
    unsigned char const *color;
297,028✔
546

547
    self = NULL;
471,137✔
548
    flag = NULL;
471,137✔
549
    color = NULL;
471,137✔
550
    if (component == NULL) {
471,137✔
551
        return SIXEL_BAD_ARGUMENT;
×
552
    }
553

554
    self = (sixel_loader_quicklook_component_t *)component;
471,137✔
555
    switch (option) {
471,137✔
556
    case SIXEL_LOADER_OPTION_REQUIRE_STATIC:
557
        flag = (int const *)value;
24,803✔
558
        self->fstatic = flag != NULL ? *flag : 0;
24,803!
559
        return SIXEL_OK;
24,803✔
560
    case SIXEL_LOADER_OPTION_USE_PALETTE:
561
        flag = (int const *)value;
24,803✔
562
        self->fuse_palette = flag != NULL ? *flag : 0;
24,803!
563
        return SIXEL_OK;
24,803✔
564
    case SIXEL_LOADER_OPTION_REQCOLORS:
565
        flag = (int const *)value;
24,803✔
566
        if (flag != NULL) {
24,803!
567
            self->reqcolors = *flag;
24,803✔
568
        }
24,803✔
569
        return SIXEL_OK;
24,803✔
570
    case SIXEL_LOADER_OPTION_BGCOLOR:
571
        if (value == NULL) {
24,803✔
572
            self->has_bgcolor = 0;
24,723✔
573
            return SIXEL_OK;
24,723✔
574
        }
575
        color = (unsigned char const *)value;
80✔
576
        self->bgcolor[0] = color[0];
80✔
577
        self->bgcolor[1] = color[1];
80✔
578
        self->bgcolor[2] = color[2];
80✔
579
        self->has_bgcolor = 1;
80✔
580
        return SIXEL_OK;
80✔
581
    case SIXEL_LOADER_OPTION_LOOP_CONTROL:
582
        flag = (int const *)value;
24,795✔
583
        if (flag != NULL) {
24,795!
584
            self->loop_control = *flag;
24,795✔
585
        }
24,795✔
586
        return SIXEL_OK;
24,795✔
587
    case SIXEL_LOADER_OPTION_START_FRAME_NO:
588
        if (value == NULL) {
24,795✔
589
            self->has_start_frame_no = 0;
23,959✔
590
            self->start_frame_no = INT_MIN;
23,959✔
591
            return SIXEL_OK;
23,959✔
592
        }
593
        flag = (int const *)value;
836✔
594
        self->start_frame_no = *flag;
836✔
595
        self->has_start_frame_no = 1;
836✔
596
        return SIXEL_OK;
836✔
597
    default:
598
        return SIXEL_OK;
322,335✔
599
    }
600
}
471,137✔
601

602
static SIXELSTATUS
603
sixel_loader_quicklook_load(sixel_loader_component_t *component,
888✔
604
                            sixel_chunk_t const *chunk,
605
                            sixel_load_image_function fn_load,
606
                            void *context)
607
{
608
    sixel_loader_quicklook_component_t *self;
555✔
609
    unsigned char *bgcolor;
555✔
610
    SIXELSTATUS status;
555✔
611
    int header_job_id;
555✔
612
    int decode_job_id;
555✔
613
    sixel_loader_timeline_callback_state_t timeline_state;
555✔
614

615
    self = NULL;
888✔
616
    bgcolor = NULL;
888✔
617
    status = SIXEL_FALSE;
888✔
618
    header_job_id = -1;
888✔
619
    decode_job_id = -1;
888✔
620
    if (component == NULL || chunk == NULL || fn_load == NULL) {
888!
621
        return SIXEL_BAD_ARGUMENT;
×
622
    }
623

624
    self = (sixel_loader_quicklook_component_t *)component;
888✔
625
    if (self->has_bgcolor) {
888✔
626
        bgcolor = self->bgcolor;
8✔
627
    }
8✔
628

629
    header_job_id = loader_timeline_phase_start("header/read");
888✔
630
    decode_job_id = loader_timeline_phase_start("decode/pixels");
888✔
631
    loader_timeline_callback_state_init(&timeline_state,
888✔
632
                                        fn_load,
888✔
633
                                        context,
888✔
634
                                        header_job_id,
888✔
635
                                        decode_job_id);
888✔
636

637
    status = load_with_quicklook(chunk,
1,776✔
638
                                 self->allocator,
888✔
639
                                 self->fstatic,
888✔
640
                                 self->fuse_palette,
888✔
641
                                 self->reqcolors,
888✔
642
                                 bgcolor,
888✔
643
                                 self->loop_control,
888✔
644
                                 self->has_start_frame_no,
888✔
645
                                 self->start_frame_no,
888✔
646
                                 loader_timeline_emit_frame_callback,
647
                                 &timeline_state);
648

649
    loader_timeline_callback_close_header(&timeline_state, status);
888✔
650
    loader_timeline_callback_close_decode(&timeline_state, status);
888✔
651
    loader_timeline_optional_skip_if_unmarked("post/colorspace");
888✔
652
    loader_timeline_optional_skip_if_unmarked("post/background");
888✔
653
    loader_timeline_optional_skip_if_unmarked("post/icc");
888✔
654

655
    return status;
888✔
656
}
888✔
657

658
static char const *
659
sixel_loader_quicklook_name(sixel_loader_component_t const *component)
25,675✔
660
{
661
    (void)component;
25,675✔
662
    return "quicklook";
25,675✔
663
}
664

665
static int
666
sixel_loader_quicklook_predicate(sixel_loader_component_t *component,
472✔
667
                                 sixel_chunk_t const *chunk)
668
{
669
    (void)component;
472✔
670
    return loader_quicklook_can_decode_chunk(chunk);
472✔
671
}
672

673
static sixel_loader_component_vtbl_t const g_sixel_loader_quicklook_vtbl = {
674
    sixel_loader_quicklook_ref,
675
    sixel_loader_quicklook_unref,
676
    sixel_loader_quicklook_setopt,
677
    sixel_loader_quicklook_load,
678
    sixel_loader_quicklook_name,
679
    sixel_loader_quicklook_predicate
680
};
681

682
SIXELSTATUS
683
sixel_loader_quicklook_new(sixel_allocator_t *allocator,
24,803✔
684
                           void **ppcomponent)
685
{
686
    sixel_loader_quicklook_component_t *self;
15,637✔
687

688
    self = NULL;
24,803✔
689
    if (allocator == NULL || ppcomponent == NULL) {
24,803!
690
        return SIXEL_BAD_ARGUMENT;
×
691
    }
692

693
    *ppcomponent = NULL;
24,803✔
694
    self = (sixel_loader_quicklook_component_t *)
24,803✔
695
        sixel_allocator_malloc(allocator, sizeof(*self));
24,803✔
696
    if (self == NULL) {
24,803✔
697
        return SIXEL_BAD_ALLOCATION;
×
698
    }
699

700
    memset(self, 0, sizeof(*self));
24,803✔
701
    self->base.vtbl = &g_sixel_loader_quicklook_vtbl;
24,803✔
702
    self->allocator = allocator;
24,803✔
703
    self->ref = 1u;
24,803✔
704
    self->reqcolors = SIXEL_PALETTE_MAX;
24,803✔
705
    self->loop_control = SIXEL_LOOP_AUTO;
24,803✔
706
    self->start_frame_no = INT_MIN;
24,803✔
707
    sixel_allocator_ref(allocator);
24,803✔
708
    *ppcomponent = &self->base;
24,803✔
709
    return SIXEL_OK;
24,803✔
710
}
24,803✔
711

712
#endif  /* HAVE_COREGRAPHICS && HAVE_QUICKLOOK */
713

714
#if !(HAVE_COREGRAPHICS && HAVE_QUICKLOOK)
715
/*
716
 * Preserve a non-empty unit when QuickLook support is disabled to avoid
717
 * pedantic compiler warnings.
718
 */
719
typedef int loader_quicklook_disabled;
720
#endif
721

722
/* emacs Local Variables:      */
723
/* emacs mode: c               */
724
/* emacs tab-width: 4          */
725
/* emacs indent-tabs-mode: nil */
726
/* emacs c-basic-offset: 4     */
727
/* emacs End:                  */
728
/* vim: set expandtab ts=4 sts=4 sw=4 : */
729
/* 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