• 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

43.61
/src/frame.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2025 libsixel developers. See `AUTHORS`.
5
 * Copyright (c) 2014-2020 Hayaki Saito
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8
 * this software and associated documentation files (the "Software"), to deal in
9
 * the Software without restriction, including without limitation the rights to
10
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
 * the Software, and to permit persons to whom the Software is furnished to do so,
12
 * subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in all
15
 * 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, FITNESS
19
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
 */
24

25
#include "config.h"
26

27
/* STDC_HEADERS */
28
#include <string.h>
29
#include <stdlib.h>
30
#include <stdio.h>
31

32
#if HAVE_MATH_H
33
# include <math.h>
34
#endif  /* HAVE_MATH_H */
35
#if HAVE_LIMITS_H
36
# include <limits.h>
37
#endif  /* HAVE_LIMITS_H */
38
#if HAVE_INTTYPES_H
39
# include <inttypes.h>
40
#endif  /* HAVE_INTTYPES_H */
41

42
#include "frame.h"
43
#include "pixelformat.h"
44
#include "compat_stub.h"
45
#include "scale.h"
46

47
#if !defined(HAVE_MEMMOVE)
48
# define memmove(d, s, n) (bcopy ((s), (d), (n)))
49
#endif
50

51
static SIXELSTATUS
52
sixel_frame_convert_to_rgb888(sixel_frame_t /*in */ *frame);
53
static SIXELSTATUS
54
sixel_frame_promote_to_float32(sixel_frame_t *frame);
55
static int
56
sixel_frame_colorspace_from_pixelformat(int pixelformat);
57
static void
58
sixel_frame_apply_pixelformat(sixel_frame_t *frame, int pixelformat);
59
static SIXELSTATUS
60
sixel_frame_validate_size(char const *context,
61
                          int width,
62
                          int height,
63
                          int pixelformat,
64
                          size_t *pixel_total,
65
                          size_t *byte_total,
66
                          int *depth_bytes);
67

68

69
/*
70
 * Validate that a frame of the requested size stays within allocation
71
 * limits. Callers receive optional pixel and byte counts so they can reuse
72
 * the calculation without recomputing the totals when allocating buffers.
73
 */
74
static SIXELSTATUS
75
sixel_frame_validate_size(char const *context,
161✔
76
                          int width,
77
                          int height,
78
                          int pixelformat,
79
                          size_t *pixel_total,
80
                          size_t *byte_total,
81
                          int *depth_bytes)
82
{
83
    SIXELSTATUS status;
161✔
84
    char message[128];
161✔
85
    size_t pixels;
161✔
86
    size_t bytes;
161✔
87
    int depth;
161✔
88

89
    status = SIXEL_FALSE;
161✔
90
    pixels = 0u;
161✔
91
    bytes = 0u;
161✔
92
    depth = 0;
161✔
93
    if (context == NULL) {
161!
94
        context = "sixel frame validation";
×
95
    }
96

97
    depth = sixel_helper_compute_depth(pixelformat);
161✔
98
    if (depth <= 0) {
161!
99
        sixel_compat_snprintf(message,
×
100
                              sizeof(message),
101
                              "%s: pixelformat depth is invalid.",
102
                              context);
103
        sixel_helper_set_additional_message(message);
×
104
        status = SIXEL_BAD_ARGUMENT;
×
105
        goto end;
×
106
    }
107

108
    pixels = (size_t)width * (size_t)height;
161✔
109
    if (pixels > SIZE_MAX / (size_t)depth) {
161!
110
        sixel_compat_snprintf(message,
×
111
                              sizeof(message),
112
                              "%s: buffer size overflow.",
113
                              context);
114
        sixel_helper_set_additional_message(message);
×
115
        status = SIXEL_BAD_INPUT;
×
116
        goto end;
×
117
    }
118

119
    bytes = pixels * (size_t)depth;
161✔
120
    if (bytes > SIXEL_ALLOCATE_BYTES_MAX) {
161!
121
        sixel_compat_snprintf(message,
×
122
                              sizeof(message),
123
                              "%s: frame exceeds "
124
                              "SIXEL_ALLOCATE_BYTES_MAX.",
125
                              context);
126
        sixel_helper_set_additional_message(message);
×
127
        status = SIXEL_BAD_ALLOCATION;
×
128
        goto end;
×
129
    }
130

131
    if (pixel_total != NULL) {
161!
132
        *pixel_total = pixels;
161✔
133
    }
56✔
134
    if (byte_total != NULL) {
161!
135
        *byte_total = bytes;
161✔
136
    }
56✔
137
    if (depth_bytes != NULL) {
161!
138
        *depth_bytes = depth;
161✔
139
    }
56✔
140

141
    status = SIXEL_OK;
56✔
142

143
end:
105✔
144
    return status;
217✔
145
}
56✔
146

147
/* constructor of frame object */
148
SIXELAPI SIXELSTATUS
149
sixel_frame_new(
867✔
150
    sixel_frame_t       /* out */ **ppframe,    /* frame object to be created */
151
    sixel_allocator_t   /* in */  *allocator)   /* allocator, null if you use
152
                                                   default allocator */
153
{
154
    SIXELSTATUS status = SIXEL_FALSE;
867✔
155

156
    if (allocator == NULL) {
867!
157
        status = sixel_allocator_new(&allocator, malloc, calloc, realloc, free);
×
158
        if (SIXEL_FAILED(status)) {
×
159
            goto end;
×
160
        }
161
    }
162

163
    *ppframe = (sixel_frame_t *)sixel_allocator_malloc(
867✔
164
        allocator,
264✔
165
        sizeof(sixel_frame_t));
166
    if (*ppframe == NULL) {
867!
167
        sixel_helper_set_additional_message(
×
168
            "sixel_frame_resize: sixel_allocator_malloc() failed.");
169
        status = SIXEL_BAD_ALLOCATION;
×
170
        goto end;
×
171
    }
172

173
    (*ppframe)->ref = 1;
867✔
174
    (*ppframe)->pixels.u8ptr = NULL;
867✔
175
    (*ppframe)->palette = NULL;
867✔
176
    (*ppframe)->width = 0;
867✔
177
    (*ppframe)->height = 0;
867✔
178
    (*ppframe)->ncolors = (-1);
867✔
179
    (*ppframe)->pixelformat = SIXEL_PIXELFORMAT_RGB888;
867✔
180
    (*ppframe)->delay = 0;
867✔
181
    (*ppframe)->frame_no = 0;
867✔
182
    (*ppframe)->loop_count = 0;
867✔
183
    (*ppframe)->multiframe = 0;
867✔
184
    (*ppframe)->transparent = (-1);
867✔
185
    (*ppframe)->allocator = allocator;
867✔
186

187
    sixel_allocator_ref(allocator);
867✔
188

189
    /* Normalize between byte and float pipelines when buffers are present. */
190
    status = SIXEL_OK;
867✔
191

192
end:
603✔
193
    return status;
1,131✔
194
}
264✔
195

196

197
SIXELAPI /* deprecated */ sixel_frame_t *
198
sixel_frame_create(void)
×
199
{
200
    SIXELSTATUS status = SIXEL_FALSE;
×
201
    sixel_frame_t *frame = NULL;
×
202

203
    status = sixel_frame_new(&frame, NULL);
×
204
    if (SIXEL_FAILED(status)) {
×
205
        goto end;
206
    }
207

208
end:
209
    return frame;
×
210
}
211

212

213
static void
214
sixel_frame_destroy(sixel_frame_t /* in */ *frame)
863✔
215
{
216
    sixel_allocator_t *allocator = NULL;
863✔
217

218
    if (frame) {
863!
219
        allocator = frame->allocator;
863✔
220
        sixel_allocator_free(allocator, frame->pixels.u8ptr);
863✔
221
        sixel_allocator_free(allocator, frame->palette);
863✔
222
        sixel_allocator_free(allocator, frame);
863✔
223
        sixel_allocator_unref(allocator);
863✔
224
    }
263✔
225
}
863✔
226

227

228
/* increase reference count of frame object (thread-unsafe) */
229
SIXELAPI void
230
sixel_frame_ref(sixel_frame_t *frame)
1,424✔
231
{
232
    /* TODO: be thread safe */
233
    ++frame->ref;
1,424✔
234
}
1,159✔
235

236

237
/* decrease reference count of frame object (thread-unsafe) */
238
SIXELAPI void
239
sixel_frame_unref(sixel_frame_t *frame)
2,404✔
240
{
241
    /* TODO: be thread safe */
242
    if (frame != NULL && --frame->ref == 0) {
2,404✔
243
        sixel_frame_destroy(frame);
863✔
244
    }
263✔
245
}
2,404✔
246

247

248
/*
249
 * Shared initializer for both byte and float32 buffers.  Keeping the
250
 * validation and field updates in one place avoids diverging logic
251
 * between the two entry points while making the stored pointer type
252
 * explicit.
253
 */
254
static SIXELSTATUS
255
sixel_frame_init_common(
4✔
256
    sixel_frame_t   *frame,
257
    void            *pixels,
258
    int              width,
259
    int              height,
260
    int              pixelformat,
261
    unsigned char   *palette,
262
    int              ncolors,
263
    int              is_float)
264
{
265
    SIXELSTATUS status = SIXEL_FALSE;
4✔
266
    size_t unused_pixel_total;
4✔
267
    size_t unused_byte_total;
4✔
268
    int unused_depth_bytes;
4✔
269

270
    sixel_frame_ref(frame);
4✔
271

272
    unused_pixel_total = 0u;
4✔
273
    unused_byte_total = 0u;
4✔
274
    unused_depth_bytes = 0;
4✔
275

276
    /* check parameters */
277
    if (width <= 0) {
4!
278
        sixel_helper_set_additional_message(
×
279
            "sixel_frame_init: an invalid width parameter detected.");
280
        status = SIXEL_BAD_INPUT;
×
281
        goto end;
×
282
    }
283
    status = sixel_frame_validate_size("sixel_frame_init",
4✔
284
                                       width,
1✔
285
                                       height,
1✔
286
                                       pixelformat,
1✔
287
                                       &unused_pixel_total,
288
                                       &unused_byte_total,
289
                                       &unused_depth_bytes);
290
    if (SIXEL_FAILED(status)) {
4!
291
        goto end;
×
292
    }
293
    if (height <= 0) {
4!
294
        sixel_helper_set_additional_message(
×
295
            "sixel_frame_init: an invalid width parameter detected.");
296
        status = SIXEL_BAD_INPUT;
×
297
        goto end;
×
298
    }
299
    if (width > SIXEL_WIDTH_LIMIT) {
4!
300
        sixel_helper_set_additional_message(
×
301
            "sixel_frame_init: given width parameter is too huge.");
302
        status = SIXEL_BAD_INPUT;
×
303
        goto end;
×
304
    }
305
    if (height > SIXEL_HEIGHT_LIMIT) {
4!
306
        sixel_helper_set_additional_message(
×
307
            "sixel_frame_init: given height parameter is too huge.");
308
        status = SIXEL_BAD_INPUT;
×
309
        goto end;
×
310
    }
311
    if (is_float != 0 && !SIXEL_PIXELFORMAT_IS_FLOAT32(pixelformat)) {
4!
312
        sixel_helper_set_additional_message(
×
313
            "sixel_frame_init: pixelformat must be float32 when "
314
            "supplying float pixels.");
315
        status = SIXEL_BAD_INPUT;
×
316
        goto end;
×
317
    }
318

319
    if (is_float != 0) {
1!
320
        frame->pixels.f32ptr = (float *)pixels;
×
321
    } else {
322
        frame->pixels.u8ptr = (unsigned char *)pixels;
4✔
323
    }
324
    frame->width = width;
4✔
325
    frame->height = height;
4✔
326
    sixel_frame_apply_pixelformat(frame, pixelformat);
7✔
327
    frame->palette = palette;
4✔
328
    frame->ncolors = ncolors;
4✔
329
    status = SIXEL_OK;
4✔
330

331
end:
3✔
332
    sixel_frame_unref(frame);
4✔
333

334
    return status;
5✔
335
}
1✔
336

337
/* initialize frame object with a pixel buffer */
338
SIXELAPI SIXELSTATUS
339
sixel_frame_init(
4✔
340
    sixel_frame_t   /* in */ *frame,
341
    unsigned char   /* in */ *pixels,
342
    int             /* in */ width,
343
    int             /* in */ height,
344
    int             /* in */ pixelformat,
345
    unsigned char   /* in */ *palette,
346
    int             /* in */ ncolors)
347
{
348
    return sixel_frame_init_common(frame,
5✔
349
                                   pixels,
1✔
350
                                   width,
1✔
351
                                   height,
1✔
352
                                   pixelformat,
1✔
353
                                   palette,
1✔
354
                                   ncolors,
1✔
355
                                   0);
356
}
357

358
/* initialize frame object with a float32 pixel buffer */
359
SIXELAPI SIXELSTATUS
360
sixel_frame_init_float32(
×
361
    sixel_frame_t   /* in */ *frame,
362
    float           /* in */ *pixels,
363
    int             /* in */ width,
364
    int             /* in */ height,
365
    int             /* in */ pixelformat,
366
    unsigned char   /* in */ *palette,
367
    int             /* in */ ncolors)
368
{
369
    return sixel_frame_init_common(frame,
×
370
                                   pixels,
371
                                   width,
372
                                   height,
373
                                   pixelformat,
374
                                   palette,
375
                                   ncolors,
376
                                   1);
377
}
378

379

380
/* get pixels */
381
SIXELAPI unsigned char *
382
sixel_frame_get_pixels(sixel_frame_t /* in */ *frame)  /* frame object */
1,462✔
383
{
384
    return frame->pixels.u8ptr;
1,462✔
385
}
386

387

388
SIXELAPI float *
389
sixel_frame_get_pixels_float32(sixel_frame_t /* in */ *frame)
×
390
{
391
    return frame->pixels.f32ptr;
×
392
}
393

394

395
/* set pixels */
396
SIXELAPI void
397
sixel_frame_set_pixels(
966✔
398
    sixel_frame_t  /* in */ *frame,
399
    unsigned char  /* in */ *pixels)
400
{
401
    frame->pixels.u8ptr = pixels;
966✔
402
}
966✔
403

404

405
SIXELAPI void
406
sixel_frame_set_pixels_float32(
×
407
    sixel_frame_t  /* in */ *frame,
408
    float          /* in */ *pixels)
409
{
410
    frame->pixels.f32ptr = pixels;
×
411
}
×
412

413

414
/* get palette */
415
SIXELAPI unsigned char *
416
sixel_frame_get_palette(sixel_frame_t /* in */ *frame)  /* frame object */
800✔
417
{
418
    return frame->palette;
800✔
419
}
420

421

422
/* set palette */
423
SIXELAPI void
424
sixel_frame_set_palette(
32✔
425
    sixel_frame_t  /* in */ *frame,
426
    unsigned char  /* in */ *palette)
427
{
428
    frame->palette = palette;
32✔
429
}
32✔
430

431

432
/* get width */
433
SIXELAPI int
434
sixel_frame_get_width(sixel_frame_t /* in */ *frame)  /* frame object */
7,834✔
435
{
436
    return frame->width;
7,834✔
437
}
438

439

440
/* set width */
441
SIXELAPI void
442
sixel_frame_set_width(sixel_frame_t /* in */ *frame, int /* in */ width)
164✔
443
{
444
    frame->width = width;
164✔
445
}
164✔
446

447

448
/* get height */
449
SIXELAPI int
450
sixel_frame_get_height(sixel_frame_t /* in */ *frame)  /* frame object */
7,978✔
451
{
452
    return frame->height;
7,978✔
453
}
454

455

456
/* set height */
457
SIXELAPI void
458
sixel_frame_set_height(sixel_frame_t /* in */ *frame, int /* in */ height)
164✔
459
{
460
    frame->height = height;
164✔
461
}
164✔
462

463

464
/* get ncolors */
465
SIXELAPI int
466
sixel_frame_get_ncolors(sixel_frame_t /* in */ *frame)  /* frame object */
482✔
467
{
468
    return frame->ncolors;
482✔
469
}
470

471

472
/* set ncolors */
473
SIXELAPI void
474
sixel_frame_set_ncolors(
164✔
475
    sixel_frame_t  /* in */ *frame,
476
    int            /* in */ ncolors)
477
{
478
    frame->ncolors = ncolors;
164✔
479
}
164✔
480

481

482
/* get pixelformat */
483
SIXELAPI int
484
sixel_frame_get_pixelformat(sixel_frame_t /* in */ *frame)  /* frame object */
3,082✔
485
{
486
    return frame->pixelformat;
3,082✔
487
}
488

489

490
/* set pixelformat */
491
SIXELAPI SIXELSTATUS
492
sixel_frame_set_pixelformat(
192✔
493
    sixel_frame_t  /* in */ *frame,
494
    int            /* in */ pixelformat)
495
{
496
    SIXELSTATUS status;
192✔
497
    int source_colorspace;
192✔
498
    int target_colorspace;
192✔
499
    int working_pixelformat;
192✔
500
    int depth;
192✔
501
    size_t pixel_total;
192✔
502
    size_t pixel_size;
192✔
503
    unsigned char *pixels;
192✔
504

505
    if (frame == NULL) {
192!
506
        sixel_helper_set_additional_message(
×
507
            "sixel_frame_set_pixelformat: frame is null.");
508
        return SIXEL_BAD_ARGUMENT;
×
509
    }
510
    if (pixelformat == frame->pixelformat) {
192✔
511
        return SIXEL_OK;
38✔
512
    }
513
    if (frame->pixels.u8ptr == NULL) {
28✔
514
        sixel_frame_apply_pixelformat(frame, pixelformat);
35✔
515
        return SIXEL_OK;
20✔
516
    }
517

518
    status = SIXEL_OK;
8✔
519
    working_pixelformat = frame->pixelformat;
8✔
520
    source_colorspace = frame->colorspace;
8✔
521

522
    /*
523
     * Palette and byte-form buffers need to be normalized before any
524
     * colorspace adjustments so that channel ordering matches the
525
     * converter's expectations.
526
     */
527
    if (pixelformat == SIXEL_PIXELFORMAT_RGBFLOAT32
13!
528
            || pixelformat == SIXEL_PIXELFORMAT_LINEARRGBFLOAT32
5!
529
            || pixelformat == SIXEL_PIXELFORMAT_OKLABFLOAT32
5!
530
            || pixelformat == SIXEL_PIXELFORMAT_CIELABFLOAT32
5!
531
            || pixelformat == SIXEL_PIXELFORMAT_DIN99DFLOAT32) {
8!
532
        if (working_pixelformat & SIXEL_FORMATTYPE_PALETTE) {
×
533
            status = sixel_frame_convert_to_rgb888(frame);
×
534
        }
535
        if (SIXEL_SUCCEEDED(status)
×
536
                && !SIXEL_PIXELFORMAT_IS_FLOAT32(frame->pixelformat)) {
×
537
            status = sixel_frame_promote_to_float32(frame);
×
538
        }
539
    } else if (pixelformat == SIXEL_PIXELFORMAT_RGB888
8!
540
            && working_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
8!
541
        status = sixel_frame_convert_to_rgb888(frame);
8✔
542
    } else if (!SIXEL_PIXELFORMAT_IS_FLOAT32(pixelformat)
5!
543
            && !SIXEL_PIXELFORMAT_IS_FLOAT32(working_pixelformat)
×
544
            && (working_pixelformat & SIXEL_FORMATTYPE_PALETTE)) {
×
545
        status = sixel_frame_convert_to_rgb888(frame);
×
546
    }
547

548
    if (SIXEL_FAILED(status)) {
8!
549
        return status;
×
550
    }
551

552
    working_pixelformat = frame->pixelformat;
8✔
553
    source_colorspace = frame->colorspace;
8✔
554
    target_colorspace = sixel_frame_colorspace_from_pixelformat(pixelformat);
11✔
555

556
    if (target_colorspace != source_colorspace) {
8!
557
        /*
558
         * Convert in-place so callers can request alternate transfer
559
         * curves or OKLab buffers without mutating the frame twice.
560
         */
561
        if (frame->width <= 0 || frame->height <= 0) {
×
562
            sixel_helper_set_additional_message(
×
563
                "sixel_frame_set_pixelformat: invalid frame size.");
564
            return SIXEL_BAD_INPUT;
×
565
        }
566

567
        pixel_total = (size_t)frame->width * (size_t)frame->height;
×
568
        if (pixel_total / (size_t)frame->width != (size_t)frame->height) {
×
569
            sixel_helper_set_additional_message(
×
570
                "sixel_frame_set_pixelformat: buffer overflow risk.");
571
            return SIXEL_BAD_INPUT;
×
572
        }
573

574
        depth = sixel_helper_compute_depth(working_pixelformat);
×
575
        if (depth <= 0) {
×
576
            sixel_helper_set_additional_message(
×
577
                "sixel_frame_set_pixelformat: invalid pixelformat depth.");
578
            return SIXEL_BAD_INPUT;
×
579
        }
580
        if (pixel_total > SIZE_MAX / (size_t)depth) {
×
581
            sixel_helper_set_additional_message(
×
582
                "sixel_frame_set_pixelformat: buffer size overflow.");
583
            return SIXEL_BAD_INPUT;
×
584
        }
585
        pixel_size = pixel_total * (size_t)depth;
×
586

587
        pixels = frame->pixels.u8ptr;
×
588
        if (SIXEL_PIXELFORMAT_IS_FLOAT32(working_pixelformat)) {
×
589
            pixels = (unsigned char *)frame->pixels.f32ptr;
×
590
        }
591

592
        status = sixel_helper_convert_colorspace(pixels,
×
593
                                                 pixel_size,
594
                                                 working_pixelformat,
595
                                                 source_colorspace,
596
                                                 target_colorspace);
597
        if (SIXEL_FAILED(status)) {
×
598
            return status;
599
        }
600
    }
601

602
    sixel_frame_apply_pixelformat(frame, pixelformat);
8✔
603
    return SIXEL_OK;
8✔
604
}
48✔
605

606

607
SIXELAPI int
608
sixel_frame_get_colorspace(sixel_frame_t /* in */ *frame)  /* frame object */
2,386✔
609
{
610
    return frame->colorspace;
2,386✔
611
}
612

613

614
/* set colorspace */
615
SIXELAPI void
616
sixel_frame_set_colorspace(
×
617
    sixel_frame_t  /* in */ *frame,
618
    int            /* in */ colorspace)
619
{
620
    frame->colorspace = colorspace;
×
621
}
×
622

623

624
/* get transparent */
625
SIXELAPI int
626
sixel_frame_get_transparent(sixel_frame_t /* in */ *frame)  /* frame object */
318✔
627
{
628
    return frame->transparent;
318✔
629
}
630

631

632
/* set transparent */
633
SIXELAPI void
634
sixel_frame_set_transparent(
×
635
    sixel_frame_t  /* in */ *frame,
636
    int            /* in */ transparent)
637
{
638
    frame->transparent = transparent;
×
639
}
×
640

641

642
/* get transparent */
643
SIXELAPI int
644
sixel_frame_get_multiframe(sixel_frame_t /* in */ *frame)  /* frame object */
794✔
645
{
646
    return frame->multiframe;
794✔
647
}
648

649

650
/* set multiframe */
651
SIXELAPI void
652
sixel_frame_set_multiframe(
164✔
653
    sixel_frame_t  /* in */ *frame,
654
    int            /* in */ multiframe)
655
{
656
    frame->multiframe = multiframe;
164✔
657
}
164✔
658

659

660
/* get delay */
661
SIXELAPI int
662
sixel_frame_get_delay(sixel_frame_t /* in */ *frame)  /* frame object */
790✔
663
{
664
    return frame->delay;
790✔
665
}
666

667

668
/* set delay */
669
SIXELAPI void
670
sixel_frame_set_delay(sixel_frame_t /* in */ *frame, int /* in */ delay)
164✔
671
{
672
    frame->delay = delay;
164✔
673
}
164✔
674

675

676
/* get frame no */
677
SIXELAPI int
678
sixel_frame_get_frame_no(sixel_frame_t /* in */ *frame)  /* frame object */
296✔
679
{
680
    return frame->frame_no;
296✔
681
}
682

683

684
/* set frame index */
685
SIXELAPI void
686
sixel_frame_set_frame_no(
36✔
687
    sixel_frame_t  /* in */ *frame,
688
    int            /* in */ frame_no)
689
{
690
    frame->frame_no = frame_no;
36✔
691
}
36✔
692

693

694
/* increment frame index */
695
SIXELAPI void
696
sixel_frame_increment_frame_no(sixel_frame_t /* in */ *frame)
152✔
697
{
698
    ++frame->frame_no;
152✔
699
}
152✔
700

701

702
/* reset frame index */
703
SIXELAPI void
704
sixel_frame_reset_frame_no(sixel_frame_t /* in */ *frame)
×
705
{
706
    frame->frame_no = 0;
×
707
}
×
708

709

710
/* get loop no */
711
SIXELAPI int
712
sixel_frame_get_loop_no(sixel_frame_t /* in */ *frame)  /* frame object */
264✔
713
{
714
    return frame->loop_count;
264✔
715
}
716

717

718
/* set loop count */
719
SIXELAPI void
720
sixel_frame_set_loop_count(
32✔
721
    sixel_frame_t  /* in */ *frame,
722
    int            /* in */ loop_count)
723
{
724
    frame->loop_count = loop_count;
32✔
725
}
32✔
726

727

728
/* increment loop count */
729
SIXELAPI void
730
sixel_frame_increment_loop_count(sixel_frame_t /* in */ *frame)
24✔
731
{
732
    ++frame->loop_count;
24✔
733
}
24✔
734

735

736
/* get allocator */
737
SIXELAPI sixel_allocator_t *
738
sixel_frame_get_allocator(sixel_frame_t /* in */ *frame)
164✔
739
{
740
    return frame->allocator;
164✔
741
}
742

743
/* strip alpha from RGBA/ARGB/BGRA/ABGR formatted pixbuf */
744
SIXELAPI SIXELSTATUS
745
sixel_frame_strip_alpha(
942✔
746
    sixel_frame_t  /* in */ *frame,
747
    unsigned char  /* in */ *bgcolor
748
)
749
{
750
    SIXELSTATUS status = SIXEL_FALSE;
942✔
751
    int i;
942✔
752
    unsigned char *src;
942✔
753
    unsigned char *dst;
942✔
754
    unsigned char alpha;
942✔
755

756
    sixel_frame_ref(frame);
942✔
757

758
    src = dst = frame->pixels.u8ptr;
942✔
759

760
    if (bgcolor) {
942✔
761
        switch (frame->pixelformat) {
40!
762
        case SIXEL_PIXELFORMAT_ARGB8888:
763
            for (i = 0; i < frame->height * frame->width; i++) {
×
764
                alpha = src[0];
×
765
                *dst++ = (*src++ * alpha + bgcolor[0] * (0xff - alpha)) >> 8;
×
766
                *dst++ = (*src++ * alpha + bgcolor[1] * (0xff - alpha)) >> 8;
×
767
                *dst++ = (*src++ * alpha + bgcolor[2] * (0xff - alpha)) >> 8;
×
768
                src++;
×
769
            }
770
            sixel_frame_apply_pixelformat(
×
771
                frame,
772
                SIXEL_PIXELFORMAT_RGB888);
773
            break;
×
774
        case SIXEL_PIXELFORMAT_RGBA8888:
775
            for (i = 0; i < frame->height * frame->width; i++) {
×
776
                alpha = src[3];
×
777
                *dst++ = (*src++ * alpha + bgcolor[0] * (0xff - alpha)) >> 8;
×
778
                *dst++ = (*src++ * alpha + bgcolor[1] * (0xff - alpha)) >> 8;
×
779
                *dst++ = (*src++ * alpha + bgcolor[2] * (0xff - alpha)) >> 8;
×
780
                src++;
×
781
            }
782
            sixel_frame_apply_pixelformat(
×
783
                frame,
784
                SIXEL_PIXELFORMAT_RGB888);
785
            break;
×
786
        case SIXEL_PIXELFORMAT_ABGR8888:
787
            for (i = 0; i < frame->height * frame->width; i++) {
×
788
                alpha = src[0];
×
789
                *dst++ = (src[3] * alpha + bgcolor[0] * (0xff - alpha)) >> 8;
×
790
                *dst++ = (src[2] * alpha + bgcolor[1] * (0xff - alpha)) >> 8;
×
791
                *dst++ = (src[1] * alpha + bgcolor[2] * (0xff - alpha)) >> 8;
×
792
                src += 4;
×
793
            }
794
            sixel_frame_apply_pixelformat(
×
795
                frame,
796
                SIXEL_PIXELFORMAT_RGB888);
797
            break;
×
798
        case SIXEL_PIXELFORMAT_BGRA8888:
799
            for (i = 0; i < frame->height * frame->width; i++) {
×
800
                alpha = src[3];
×
801
                *dst++ = (src[2] * alpha + bgcolor[0] * (0xff - alpha)) >> 8;
×
802
                *dst++ = (src[1] * alpha + bgcolor[1] * (0xff - alpha)) >> 8;
×
803
                *dst++ = (src[0] * alpha + bgcolor[2] * (0xff - alpha)) >> 8;
×
804
                src += 4;
×
805
            }
806
            sixel_frame_apply_pixelformat(
×
807
                frame,
808
                SIXEL_PIXELFORMAT_RGB888);
809
            break;
×
810
        default:
811
            break;
22✔
812
        }
813
    } else {
22✔
814
        switch (frame->pixelformat) {
902!
815
        case SIXEL_PIXELFORMAT_ARGB8888:
816
            for (i = 0; i < frame->height * frame->width; i++) {
×
817
                src++;            /* A */
×
818
                *dst++ = *src++;  /* R */
×
819
                *dst++ = *src++;  /* G */
×
820
                *dst++ = *src++;  /* B */
×
821
            }
822
            sixel_frame_apply_pixelformat(
×
823
                frame,
824
                SIXEL_PIXELFORMAT_RGB888);
825
            break;
×
826
        case SIXEL_PIXELFORMAT_RGBA8888:
827
            for (i = 0; i < frame->height * frame->width; i++) {
×
828
                *dst++ = *src++;  /* R */
×
829
                *dst++ = *src++;  /* G */
×
830
                *dst++ = *src++;  /* B */
×
831
                src++;            /* A */
×
832
            }
833
            sixel_frame_apply_pixelformat(
×
834
                frame,
835
                SIXEL_PIXELFORMAT_RGB888);
836
            break;
×
837
        case SIXEL_PIXELFORMAT_ABGR8888:
838
            for (i = 0; i < frame->height * frame->width; i++) {
×
839
                *dst++ = src[3];  /* R */
×
840
                *dst++ = src[2];  /* G */
×
841
                *dst++ = src[1];  /* B */
×
842
                src += 4;
×
843
            }
844
            sixel_frame_apply_pixelformat(
×
845
                frame,
846
                SIXEL_PIXELFORMAT_RGB888);
847
            break;
×
848
        case SIXEL_PIXELFORMAT_BGRA8888:
849
            for (i = 0; i < frame->height * frame->width; i++) {
×
850
                *dst++ = src[2];  /* R */
×
851
                *dst++ = src[1];  /* G */
×
852
                *dst++ = src[0];  /* B */
×
853
                src += 4;
×
854
            }
855
            sixel_frame_apply_pixelformat(
×
856
                frame,
857
                SIXEL_PIXELFORMAT_RGB888);
858
            break;
×
859
        default:
860
            break;
249✔
861
        }
862
    }
863

864
    status = SIXEL_OK;
942✔
865

866
    sixel_frame_unref(frame);
942✔
867

868
    return status;
1,213✔
869
}
271✔
870

871

872
static SIXELSTATUS
873
sixel_frame_convert_to_rgb888(sixel_frame_t /*in */ *frame)
144✔
874
{
875
    SIXELSTATUS status = SIXEL_FALSE;
144✔
876
    unsigned char *normalized_pixels = NULL;
144✔
877
    size_t size;
144✔
878
    unsigned char *dst;
144✔
879
    unsigned char *src;
144✔
880
    unsigned char *p;
144✔
881
    unsigned char *raw_pixels;
144✔
882
    unsigned char const *source_pixels;
144✔
883
    float *float_pixels;
144✔
884

885
    sixel_frame_ref(frame);
144✔
886

887
    raw_pixels = frame->pixels.u8ptr;
144✔
888
    float_pixels = frame->pixels.f32ptr;
144✔
889
    source_pixels = raw_pixels;
144✔
890

891
    switch (frame->pixelformat) {
144!
892
    case SIXEL_PIXELFORMAT_PAL1:
893
    case SIXEL_PIXELFORMAT_PAL2:
894
    case SIXEL_PIXELFORMAT_PAL4:
895
        size = (size_t)(frame->width * frame->height * 4);
3✔
896
        normalized_pixels = (unsigned char *)
3✔
897
            sixel_allocator_malloc(frame->allocator, size);
3✔
898
        if (normalized_pixels == NULL) {
3!
899
            sixel_helper_set_additional_message(
×
900
                "sixel_frame_convert_to_rgb888: "
901
                "sixel_allocator_malloc() failed.");
902
            status = SIXEL_BAD_ALLOCATION;
×
903
            goto end;
×
904
        }
905
        src = normalized_pixels + frame->width * frame->height * 3;
3✔
906
        dst = normalized_pixels;
3✔
907
        status = sixel_helper_normalize_pixelformat(src,
6✔
908
                                                    &frame->pixelformat,
3✔
909
                                                    source_pixels,
3✔
910
                                                    frame->pixelformat,
3✔
911
                                                    frame->width,
3✔
912
                                                    frame->height);
3✔
913
        if (SIXEL_FAILED(status)) {
3!
914
            sixel_allocator_free(frame->allocator, normalized_pixels);
×
915
            goto end;
×
916
        }
917
        for (p = src; dst < src; ++p) {
27,723!
918
            *dst++ = *(frame->palette + *p * 3 + 0);
27,720✔
919
            *dst++ = *(frame->palette + *p * 3 + 1);
27,720✔
920
            *dst++ = *(frame->palette + *p * 3 + 2);
27,720✔
921
        }
27,720✔
922
        sixel_allocator_free(frame->allocator, raw_pixels);
3✔
923
        frame->pixels.u8ptr = normalized_pixels;
3✔
924
        sixel_frame_apply_pixelformat(
3✔
925
            frame,
3✔
926
            SIXEL_PIXELFORMAT_RGB888);
927
        break;
3✔
928
    case SIXEL_PIXELFORMAT_PAL8:
3✔
929
        size = (size_t)(frame->width * frame->height * 3);
4✔
930
        normalized_pixels = (unsigned char *)
4✔
931
            sixel_allocator_malloc(frame->allocator, size);
4✔
932
        if (normalized_pixels == NULL) {
4!
933
            sixel_helper_set_additional_message(
×
934
                "sixel_frame_convert_to_rgb888: "
935
                "sixel_allocator_malloc() failed.");
936
            status = SIXEL_BAD_ALLOCATION;
×
937
            goto end;
×
938
        }
939
        src = raw_pixels;
1✔
940
        dst = normalized_pixels;
1✔
941
        for (; dst != normalized_pixels + size; ++src) {
5,212✔
942
            *dst++ = frame->palette[*src * 3 + 0];
5,208✔
943
            *dst++ = frame->palette[*src * 3 + 1];
5,208✔
944
            *dst++ = frame->palette[*src * 3 + 2];
5,208✔
945
        }
1,302✔
946
        sixel_allocator_free(frame->allocator, raw_pixels);
4✔
947
        frame->pixels.u8ptr = normalized_pixels;
4✔
948
        sixel_frame_apply_pixelformat(
4✔
949
            frame,
1✔
950
            SIXEL_PIXELFORMAT_RGB888);
951
        break;
4✔
952
    case SIXEL_PIXELFORMAT_RGB888:
953
        break;
954
    case SIXEL_PIXELFORMAT_G8:
955
    case SIXEL_PIXELFORMAT_GA88:
956
    case SIXEL_PIXELFORMAT_AG88:
957
    case SIXEL_PIXELFORMAT_RGB555:
958
    case SIXEL_PIXELFORMAT_RGB565:
959
    case SIXEL_PIXELFORMAT_BGR555:
960
    case SIXEL_PIXELFORMAT_BGR565:
961
    case SIXEL_PIXELFORMAT_RGBA8888:
962
    case SIXEL_PIXELFORMAT_ARGB8888:
963
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
964
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
965
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
966
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
967
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
968
        /* normalize pixelformat */
969
        size = (size_t)(frame->width * frame->height * 3);
1✔
970
        normalized_pixels = (unsigned char *)
1✔
971
            sixel_allocator_malloc(frame->allocator, size);
1✔
972
        if (normalized_pixels == NULL) {
1!
973
            sixel_helper_set_additional_message(
×
974
                "sixel_frame_convert_to_rgb888: "
975
                "sixel_allocator_malloc() failed.");
976
            status = SIXEL_BAD_ALLOCATION;
×
977
            goto end;
×
978
        }
979
        if (SIXEL_PIXELFORMAT_IS_FLOAT32(frame->pixelformat)) {
1!
980
            source_pixels = (unsigned char const *)float_pixels;
×
981
        }
982
        status = sixel_helper_normalize_pixelformat(normalized_pixels,
2✔
983
                                                    &frame->pixelformat,
1✔
984
                                                    source_pixels,
1✔
985
                                                    frame->pixelformat,
1✔
986
                                                    frame->width,
1✔
987
                                                    frame->height);
1✔
988
        if (SIXEL_FAILED(status)) {
1!
989
            sixel_allocator_free(frame->allocator, normalized_pixels);
×
990
            goto end;
×
991
        }
992
        sixel_allocator_free(frame->allocator, raw_pixels);
1✔
993
        frame->pixels.u8ptr = normalized_pixels;
1✔
994
        break;
1✔
995
    default:
996
        status = SIXEL_LOGIC_ERROR;
×
997
        sixel_helper_set_additional_message(
×
998
            "sixel_frame_convert_to_rgb888: invalid pixelformat.");
999
        goto end;
×
1000
    }
1001

1002
    status = SIXEL_OK;
39✔
1003

1004
end:
105✔
1005
    sixel_frame_unref(frame);
144✔
1006

1007
    return status;
183✔
1008
}
39✔
1009

1010
/*
1011
 * Infer colorspace metadata from the pixelformat.  Float formats encode
1012
 * their transfer characteristics directly, while byte-oriented formats
1013
 * default to gamma encoded RGB.
1014
 */
1015
static int
1016
sixel_frame_colorspace_from_pixelformat(int pixelformat)
41✔
1017
{
1018
    switch (pixelformat) {
23!
1019
    case SIXEL_PIXELFORMAT_LINEARRGBFLOAT32:
1020
        return SIXEL_COLORSPACE_LINEAR;
1021
    case SIXEL_PIXELFORMAT_OKLABFLOAT32:
1022
        return SIXEL_COLORSPACE_OKLAB;
1023
    case SIXEL_PIXELFORMAT_CIELABFLOAT32:
1024
        return SIXEL_COLORSPACE_CIELAB;
1025
    case SIXEL_PIXELFORMAT_DIN99DFLOAT32:
1026
        return SIXEL_COLORSPACE_DIN99D;
1027
    default:
1028
        return SIXEL_COLORSPACE_GAMMA;
20✔
1029
    }
1030
}
20✔
1031

1032
static void
1033
sixel_frame_apply_pixelformat(sixel_frame_t *frame, int pixelformat)
39✔
1034
{
1035
    frame->pixelformat = pixelformat;
39✔
1036
    frame->colorspace = sixel_frame_colorspace_from_pixelformat(pixelformat);
36!
1037
}
18✔
1038

1039
#if HAVE_DIAGNOSTIC_UNUSED_FUNCTION
1040
# pragma GCC diagnostic push
1041
# pragma GCC diagnostic ignored "-Wunused-function"
1042
#endif
1043

1044
/*
1045
 * Select the float pixelformat that matches the frame's current colorspace
1046
 * so downstream conversions interpret each channel correctly.  OKLab uses
1047
 * a [-0.5, 0.5] range for a/b, while gamma/linear share the 0-1 interval.
1048
 */
1049
static int
1050
sixel_frame_float_pixelformat_for_colorspace(int colorspace)
×
1051
{
1052
    switch (colorspace) {
×
1053
    case SIXEL_COLORSPACE_LINEAR:
1054
        return SIXEL_PIXELFORMAT_LINEARRGBFLOAT32;
1055
    case SIXEL_COLORSPACE_OKLAB:
1056
        return SIXEL_PIXELFORMAT_OKLABFLOAT32;
1057
    case SIXEL_COLORSPACE_CIELAB:
1058
        return SIXEL_PIXELFORMAT_CIELABFLOAT32;
1059
    case SIXEL_COLORSPACE_DIN99D:
1060
        return SIXEL_PIXELFORMAT_DIN99DFLOAT32;
1061
    default:
1062
        return SIXEL_PIXELFORMAT_RGBFLOAT32;
1063
    }
1064
}
1065

1066
static SIXELSTATUS
1067
sixel_frame_promote_to_float32(sixel_frame_t *frame)
×
1068
{
1069
    float *float_pixels;
×
1070
    unsigned char *byte_pixels;
×
1071
    unsigned char const *pixel;
×
1072
    size_t pixel_total;
×
1073
    size_t bytes;
×
1074
    size_t index;
×
1075
    size_t base;
×
1076
    int float_pixelformat;
×
1077
    int step;
×
1078
    int index_r;
×
1079
    int index_g;
×
1080
    int index_b;
×
1081

1082
    step = 0;
×
1083
    index_r = 0;
×
1084
    index_g = 0;
×
1085
    index_b = 0;
×
1086

1087
    /*
1088
     * Derive the byte stride and per-channel offsets instead of coercing the
1089
     * frame to RGB888 first.  OKLab buffers keep their signed A/B buckets in
1090
     * the order dictated by pixelformat, so preserving that layout avoids the
1091
     * blue casts reported when we reinterpreted them as gamma RGB.
1092
     */
1093
    switch (frame->pixelformat) {
×
1094
    case SIXEL_PIXELFORMAT_RGB888:
1095
        step = 3;
1096
        index_r = 0;
1097
        index_g = 1;
1098
        index_b = 2;
1099
        break;
1100
    case SIXEL_PIXELFORMAT_BGR888:
1101
        step = 3;
×
1102
        index_r = 2;
×
1103
        index_g = 1;
×
1104
        index_b = 0;
×
1105
        break;
×
1106
    case SIXEL_PIXELFORMAT_RGBA8888:
1107
        step = 4;
×
1108
        index_r = 0;
×
1109
        index_g = 1;
×
1110
        index_b = 2;
×
1111
        break;
×
1112
    case SIXEL_PIXELFORMAT_ARGB8888:
1113
        step = 4;
×
1114
        index_r = 1;
×
1115
        index_g = 2;
×
1116
        index_b = 3;
×
1117
        break;
×
1118
    case SIXEL_PIXELFORMAT_BGRA8888:
1119
        step = 4;
×
1120
        index_r = 2;
×
1121
        index_g = 1;
×
1122
        index_b = 0;
×
1123
        break;
×
1124
    case SIXEL_PIXELFORMAT_ABGR8888:
1125
        step = 4;
×
1126
        index_r = 3;
×
1127
        index_g = 2;
×
1128
        index_b = 1;
×
1129
        break;
×
1130
    case SIXEL_PIXELFORMAT_G8:
1131
        step = 1;
×
1132
        index_r = 0;
×
1133
        index_g = 0;
×
1134
        index_b = 0;
×
1135
        break;
×
1136
    case SIXEL_PIXELFORMAT_GA88:
1137
        step = 2;
×
1138
        index_r = 0;
×
1139
        index_g = 0;
×
1140
        index_b = 0;
×
1141
        break;
×
1142
    case SIXEL_PIXELFORMAT_AG88:
1143
        step = 2;
×
1144
        index_r = 1;
×
1145
        index_g = 1;
×
1146
        index_b = 1;
×
1147
        break;
×
1148
    default:
1149
        sixel_helper_set_additional_message(
×
1150
            "sixel_frame_promote_to_float32: unsupported pixelformat.");
1151
        return SIXEL_BAD_INPUT;
×
1152
    }
1153

1154
    if ((size_t)frame->width > SIZE_MAX / (size_t)frame->height) {
×
1155
        sixel_helper_set_additional_message(
×
1156
            "sixel_frame_promote_to_float32: overflow.");
1157
        return SIXEL_BAD_INPUT;
×
1158
    }
1159

1160
    pixel_total = (size_t)frame->width * (size_t)frame->height;
×
1161
    if (pixel_total > SIZE_MAX / (3U * sizeof(float))) {
×
1162
        sixel_helper_set_additional_message(
×
1163
            "sixel_frame_promote_to_float32: buffer too large.");
1164
        return SIXEL_BAD_INPUT;
×
1165
    }
1166
    bytes = pixel_total * 3U * sizeof(float);
×
1167
    float_pixels = (float *)sixel_allocator_malloc(frame->allocator, bytes);
×
1168
    if (float_pixels == NULL) {
×
1169
        sixel_helper_set_additional_message(
×
1170
            "sixel_frame_promote_to_float32: "
1171
            "sixel_allocator_malloc() failed.");
1172
        return SIXEL_BAD_ALLOCATION;
×
1173
    }
1174

1175
    byte_pixels = frame->pixels.u8ptr;
×
1176
    float_pixelformat =
×
1177
        sixel_frame_float_pixelformat_for_colorspace(frame->colorspace);
×
1178

1179
    for (index = 0U; index < pixel_total; ++index) {
×
1180
        unsigned char r8;
×
1181
        unsigned char g8;
×
1182
        unsigned char b8;
×
1183

1184
        pixel = byte_pixels + index * (size_t)step;
×
1185
        r8 = *(pixel + (size_t)index_r);
×
1186
        g8 = *(pixel + (size_t)index_g);
×
1187
        b8 = *(pixel + (size_t)index_b);
×
1188

1189
        base = index * 3U;
×
1190
        float_pixels[base + 0U] =
×
1191
            sixel_pixelformat_byte_to_float(float_pixelformat, 0, r8);
×
1192
        float_pixels[base + 1U] =
×
1193
            sixel_pixelformat_byte_to_float(float_pixelformat, 1, g8);
×
1194
        float_pixels[base + 2U] =
×
1195
            sixel_pixelformat_byte_to_float(float_pixelformat, 2, b8);
×
1196
    }
1197

1198
    sixel_allocator_free(frame->allocator, byte_pixels);
×
1199
    sixel_frame_set_pixels_float32(frame, float_pixels);
×
1200
    sixel_frame_apply_pixelformat(frame, float_pixelformat);
×
1201
    return SIXEL_OK;
×
1202
}
1203

1204
#if HAVE_DIAGNOSTIC_UNUSED_FUNCTION
1205
# pragma GCC diagnostic pop
1206
#endif
1207

1208
/* resize a frame to given size with specified resampling filter */
1209
SIXELAPI SIXELSTATUS
1210
sixel_frame_resize(
157✔
1211
    sixel_frame_t *frame,
1212
    int width,
1213
    int height,
1214
    int method_for_resampling
1215
)
1216
{
1217
    SIXELSTATUS status = SIXEL_FALSE;
157✔
1218
    size_t size;
157✔
1219
    unsigned char *scaled_frame = NULL;
157✔
1220
    size_t unused_pixel_total;
157✔
1221
    int unused_depth_bytes;
157✔
1222

1223
    sixel_frame_ref(frame);
157✔
1224

1225
    size = 0u;
157✔
1226
    unused_pixel_total = 0u;
157✔
1227
    unused_depth_bytes = 0;
157✔
1228

1229
    /* check parameters */
1230
    if (width <= 0) {
157!
1231
        sixel_helper_set_additional_message(
×
1232
            "sixel_frame_resize: an invalid width parameter detected.");
1233
        status = SIXEL_BAD_INPUT;
×
1234
        goto end;
×
1235
    }
1236
    if (height <= 0) {
157!
1237
        sixel_helper_set_additional_message(
×
1238
            "sixel_frame_resize: an invalid width parameter detected.");
1239
        status = SIXEL_BAD_INPUT;
×
1240
        goto end;
×
1241
    }
1242
    if (width > SIXEL_WIDTH_LIMIT) {
157!
1243
        sixel_helper_set_additional_message(
×
1244
            "sixel_frame_resize: given width parameter is too huge.");
1245
        status = SIXEL_BAD_INPUT;
×
1246
        goto end;
×
1247
    }
1248
    if (height > SIXEL_HEIGHT_LIMIT) {
157!
1249
        sixel_helper_set_additional_message(
×
1250
            "sixel_frame_resize: given height parameter is too huge.");
1251
        status = SIXEL_BAD_INPUT;
×
1252
        goto end;
×
1253
    }
1254

1255
    status = sixel_frame_validate_size("sixel_frame_resize",
157✔
1256
                                       width,
55✔
1257
                                       height,
55✔
1258
                                       SIXEL_PIXELFORMAT_RGB888,
1259
                                       &unused_pixel_total,
1260
                                       &size,
1261
                                       &unused_depth_bytes);
1262
    if (SIXEL_FAILED(status)) {
157!
1263
        goto end;
×
1264
    }
1265

1266
    if (width == frame->width && height == frame->height) {
157!
1267
        /* nothing to do */
1268
        goto out;
21✔
1269
    }
1270

1271
    status = sixel_frame_convert_to_rgb888(frame);
136✔
1272
    if (SIXEL_FAILED(status)) {
136!
1273
        goto end;
×
1274
    }
1275

1276
    scaled_frame = (unsigned char *)
136✔
1277
        sixel_allocator_malloc(frame->allocator, size);
136✔
1278
    if (scaled_frame == NULL) {
136!
1279
        sixel_helper_set_additional_message(
×
1280
            "sixel_frame_resize: sixel_allocator_malloc() failed.");
1281
        status = SIXEL_BAD_ALLOCATION;
×
1282
        goto end;
×
1283
    }
1284

1285
    status = sixel_helper_scale_image(
238✔
1286
        scaled_frame,
34✔
1287
        frame->pixels.u8ptr,
136✔
1288
        frame->width,
34✔
1289
        frame->height,
34✔
1290
        3,
1291
        width,
34✔
1292
        height,
34✔
1293
        method_for_resampling,
34✔
1294
        frame->allocator);
34✔
1295
    if (SIXEL_FAILED(status)) {
136!
1296
        goto end;
×
1297
    }
1298
    sixel_allocator_free(frame->allocator, frame->pixels.u8ptr);
136✔
1299
    frame->pixels.u8ptr = scaled_frame;
136✔
1300
    frame->width = width;
136✔
1301
    frame->height = height;
136✔
1302

1303
out:
1304
    status = SIXEL_OK;
55✔
1305

1306
end:
102✔
1307
    sixel_frame_unref(frame);
157✔
1308

1309
    return status;
212✔
1310
}
55✔
1311

1312
/*
1313
 * Resize a frame using float buffers. Non-nearest filters run in linear RGB
1314
 * so interpolation blends radiometrically instead of on gamma encoded values.
1315
 * Callers can still request nearest-neighbor sampling, in which case the
1316
 * routine preserves the frame's current colorspace while scaling.
1317
 */
1318
SIXELAPI SIXELSTATUS
1319
sixel_frame_resize_float32(
×
1320
    sixel_frame_t *frame,
1321
    int width,
1322
    int height,
1323
    int method_for_resampling)
1324
{
1325
    SIXELSTATUS status;
×
1326
    size_t pixel_total;
×
1327
    size_t size;
×
1328
    float *scaled_frame;
×
1329
    int depth;
×
1330
    int depth_bytes;
×
1331
    int target_pixelformat;
×
1332

1333
    status = SIXEL_FALSE;
×
1334
    scaled_frame = NULL;
×
1335
    pixel_total = 0u;
×
1336
    size = 0u;
×
1337
    depth = 0;
×
1338
    depth_bytes = 0;
×
1339
    target_pixelformat = SIXEL_PIXELFORMAT_RGBFLOAT32;
×
1340

1341
    sixel_frame_ref(frame);
×
1342

1343
    if (width <= 0) {
×
1344
        sixel_helper_set_additional_message(
×
1345
            "sixel_frame_resize_float32: "
1346
            "an invalid width parameter detected.");
1347
        status = SIXEL_BAD_INPUT;
×
1348
        goto end;
×
1349
    }
1350
    if (height <= 0) {
×
1351
        sixel_helper_set_additional_message(
×
1352
            "sixel_frame_resize_float32: "
1353
            "an invalid width parameter detected.");
1354
        status = SIXEL_BAD_INPUT;
×
1355
        goto end;
×
1356
    }
1357
    if (width > SIXEL_WIDTH_LIMIT) {
×
1358
        sixel_helper_set_additional_message(
×
1359
            "sixel_frame_resize_float32: "
1360
            "given width parameter is too huge.");
1361
        status = SIXEL_BAD_INPUT;
×
1362
        goto end;
×
1363
    }
1364
    if (height > SIXEL_HEIGHT_LIMIT) {
×
1365
        sixel_helper_set_additional_message(
×
1366
            "sixel_frame_resize_float32: "
1367
            "given height parameter is too huge.");
1368
        status = SIXEL_BAD_INPUT;
×
1369
        goto end;
×
1370
    }
1371

1372
    if (width == frame->width && height == frame->height) {
×
1373
        goto out;
×
1374
    }
1375

1376
    if (method_for_resampling == SIXEL_RES_NEAREST) {
×
1377
        target_pixelformat =
×
1378
            sixel_frame_float_pixelformat_for_colorspace(
×
1379
                frame->colorspace);
1380
    } else {
1381
        target_pixelformat = SIXEL_PIXELFORMAT_LINEARRGBFLOAT32;
1382
    }
1383

1384
    status = sixel_frame_set_pixelformat(frame, target_pixelformat);
×
1385
    if (SIXEL_FAILED(status)) {
×
1386
        goto end;
×
1387
    }
1388

1389
    status = sixel_frame_validate_size("sixel_frame_resize_float32",
×
1390
                                       width,
1391
                                       height,
1392
                                       frame->pixelformat,
1393
                                       &pixel_total,
1394
                                       &size,
1395
                                       &depth_bytes);
1396
    if (SIXEL_FAILED(status)) {
×
1397
        goto end;
×
1398
    }
1399

1400
    /*
1401
     * sixel_frame_validate_size() returns bytes per pixel. Convert the value
1402
     * to channels for validation and reuse the byte count for buffer sizing
1403
     * to avoid overflow on float formats.
1404
     */
1405
    if (depth_bytes % (int)sizeof(float) != 0) {
×
1406
        sixel_helper_set_additional_message(
×
1407
            "sixel_frame_resize_float32: "
1408
            "pixelformat depth is not float-aligned.");
1409
        status = SIXEL_BAD_ARGUMENT;
×
1410
        goto end;
×
1411
    }
1412
    depth = depth_bytes / (int)sizeof(float);
×
1413

1414
    if (depth != 3) {
×
1415
        sixel_helper_set_additional_message(
×
1416
            "sixel_frame_resize_float32: "
1417
            "unsupported channel count.");
1418
        status = SIXEL_BAD_ARGUMENT;
×
1419
        goto end;
×
1420
    }
1421

1422
    scaled_frame = (float *)sixel_allocator_malloc(frame->allocator, size);
×
1423
    if (scaled_frame == NULL) {
×
1424
        sixel_helper_set_additional_message(
×
1425
            "sixel_frame_resize_float32: "
1426
            "sixel_allocator_malloc() failed.");
1427
        status = SIXEL_BAD_ALLOCATION;
×
1428
        goto end;
×
1429
    }
1430

1431
    status = sixel_helper_scale_image_float32(
×
1432
        scaled_frame,
1433
        frame->pixels.f32ptr,
×
1434
        frame->width,
1435
        frame->height,
1436
        frame->pixelformat,
1437
        width,
1438
        height,
1439
        method_for_resampling,
1440
        frame->allocator);
1441
    if (SIXEL_FAILED(status)) {
×
1442
        goto end;
×
1443
    }
1444

1445
    sixel_allocator_free(frame->allocator, frame->pixels.f32ptr);
×
1446
    frame->pixels.f32ptr = scaled_frame;
×
1447
    frame->width = width;
×
1448
    frame->height = height;
×
1449

1450
out:
1451
    status = SIXEL_OK;
1452

1453
end:
1454
    if (SIXEL_FAILED(status) && scaled_frame != NULL) {
×
1455
        sixel_allocator_free(frame->allocator, scaled_frame);
×
1456
    }
1457
    sixel_frame_unref(frame);
×
1458

1459
    return status;
×
1460
}
1461

1462

1463
static SIXELSTATUS
1464
clip(unsigned char *pixels,
29✔
1465
     int sx,
1466
     int sy,
1467
     int pixelformat,
1468
     int cx,
1469
     int cy,
1470
     int cw,
1471
     int ch)
1472
{
1473
    SIXELSTATUS status = SIXEL_FALSE;
29✔
1474
    int y;
29✔
1475
    unsigned char *src;
29✔
1476
    unsigned char *dst;
29✔
1477
    int depth;
29✔
1478
    char message[256];
29✔
1479
    int nwrite;
29✔
1480

1481
    /* unused */ (void) sx;
29✔
1482
    /* unused */ (void) sy;
29✔
1483
    /* unused */ (void) cx;
29✔
1484

1485
    switch (pixelformat) {
29!
1486
    case SIXEL_PIXELFORMAT_PAL8:
12✔
1487
    case SIXEL_PIXELFORMAT_G8:
1488
    case SIXEL_PIXELFORMAT_RGB888:
1489
    case SIXEL_PIXELFORMAT_RGBFLOAT32:
1490
        depth = sixel_helper_compute_depth(pixelformat);
29✔
1491
        if (depth < 0) {
29!
1492
            status = SIXEL_LOGIC_ERROR;
×
1493
            /*
1494
             * We funnel formatting through the compat helper so that MSVC
1495
             * receives explicit bounds information.
1496
             */
1497
            nwrite = sixel_compat_snprintf(
×
1498
                message,
1499
                sizeof(message),
1500
                "clip: sixel_helper_compute_depth(%08x) failed.",
1501
                pixelformat);
1502
            if (nwrite > 0) {
×
1503
                sixel_helper_set_additional_message(message);
×
1504
            }
1505
            goto end;
×
1506
        }
1507

1508
        dst = pixels;
29✔
1509
        src = pixels + cy * sx * depth + cx * depth;
29✔
1510
        for (y = 0; y < ch; y++) {
2,757✔
1511
            memmove(dst, src, (size_t)(cw * depth));
2,728✔
1512
            dst += (cw * depth);
2,728✔
1513
            src += (sx * depth);
2,728✔
1514
        }
838✔
1515

1516
        status = SIXEL_OK;
17✔
1517

1518
        break;
17✔
1519
    default:
1520
        status = SIXEL_BAD_ARGUMENT;
×
1521
        nwrite = sixel_compat_snprintf(
×
1522
            message,
1523
            sizeof(message),
1524
            "clip: invalid pixelformat(%08x) is specified.",
1525
            pixelformat);
1526
        if (nwrite > 0) {
×
1527
            sixel_helper_set_additional_message(message);
×
1528
        }
1529
        break;
1530
    }
17✔
1531

1532
end:
12✔
1533
    return status;
46✔
1534
}
17✔
1535

1536

1537
/* clip frame */
1538
SIXELAPI SIXELSTATUS
1539
sixel_frame_clip(
29✔
1540
    sixel_frame_t *frame,
1541
    int x,
1542
    int y,
1543
    int width,
1544
    int height
1545
)
1546
{
1547
    SIXELSTATUS status = SIXEL_FALSE;
29✔
1548
    unsigned char *normalized_pixels;
29✔
1549
    unsigned char *raw_pixels;
29✔
1550

1551
    sixel_frame_ref(frame);
29✔
1552

1553
    raw_pixels = frame->pixels.u8ptr;
29✔
1554

1555
    /* check parameters */
1556
    if (width <= 0) {
29!
1557
        sixel_helper_set_additional_message(
×
1558
            "sixel_frame_clip: an invalid width parameter detected.");
1559
        status = SIXEL_BAD_INPUT;
×
1560
        goto end;
×
1561
    }
1562
    if (height <= 0) {
29!
1563
        sixel_helper_set_additional_message(
×
1564
            "sixel_frame_clip: an invalid width parameter detected.");
1565
        status = SIXEL_BAD_INPUT;
×
1566
        goto end;
×
1567
    }
1568
    if (width > SIXEL_WIDTH_LIMIT) {
29!
1569
        sixel_helper_set_additional_message(
×
1570
            "sixel_frame_clip: given width parameter is too huge.");
1571
        status = SIXEL_BAD_INPUT;
×
1572
        goto end;
×
1573
    }
1574
    if (height > SIXEL_HEIGHT_LIMIT) {
29!
1575
        sixel_helper_set_additional_message(
×
1576
            "sixel_frame_clip: given height parameter is too huge.");
1577
        status = SIXEL_BAD_INPUT;
×
1578
        goto end;
×
1579
    }
1580

1581
    switch (frame->pixelformat) {
29!
1582
    case SIXEL_PIXELFORMAT_PAL1:
1583
    case SIXEL_PIXELFORMAT_PAL2:
1584
    case SIXEL_PIXELFORMAT_PAL4:
1585
    case SIXEL_PIXELFORMAT_G1:
1586
    case SIXEL_PIXELFORMAT_G2:
1587
    case SIXEL_PIXELFORMAT_G4:
1588
        normalized_pixels = (unsigned char *)
3✔
1589
            sixel_allocator_malloc(frame->allocator,
6✔
1590
                                   (size_t)(frame->width * frame->height));
3✔
1591
        status = sixel_helper_normalize_pixelformat(normalized_pixels,
6✔
1592
                                                    &frame->pixelformat,
3✔
1593
                                                    raw_pixels,
3✔
1594
                                                    frame->pixelformat,
3✔
1595
                                                    frame->width,
3✔
1596
                                                    frame->height);
3✔
1597
        if (SIXEL_FAILED(status)) {
3!
1598
            sixel_allocator_free(frame->allocator, normalized_pixels);
×
1599
            goto end;
×
1600
        }
1601
        sixel_allocator_free(frame->allocator, raw_pixels);
3✔
1602
        frame->pixels.u8ptr = normalized_pixels;
3✔
1603
        raw_pixels = normalized_pixels;
3✔
1604
        break;
3✔
1605
    default:
1606
        break;
14✔
1607
    }
1608

1609
    status = clip(raw_pixels,
46✔
1610
                  frame->width,
17✔
1611
                  frame->height,
17✔
1612
                  frame->pixelformat,
17✔
1613
                  x,
17✔
1614
                  y,
17✔
1615
                  width,
17✔
1616
                  height);
17✔
1617
    if (SIXEL_FAILED(status)) {
29!
1618
        goto end;
×
1619
    }
1620
    frame->width = width;
29✔
1621
    frame->height = height;
29✔
1622

1623
    status = SIXEL_OK;
29✔
1624

1625
end:
12✔
1626
    sixel_frame_unref(frame);
29✔
1627

1628
    return status;
46✔
1629
}
17✔
1630

1631

1632
#if HAVE_TESTS
1633
static int
1634
test1(void)
×
1635
{
1636
    sixel_frame_t *frame = NULL;
×
1637
    int nret = EXIT_FAILURE;
×
1638

1639
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1640
#  pragma GCC diagnostic push
1641
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1642
#endif
1643
    frame = sixel_frame_create();
×
1644
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1645
#  pragma GCC diagnostic pop
1646
#endif
1647
    if (frame == NULL) {
×
1648
        goto error;
×
1649
    }
1650
    sixel_frame_ref(frame);
×
1651
    sixel_frame_unref(frame);
×
1652
    nret = EXIT_SUCCESS;
×
1653

1654
error:
1655
    sixel_frame_unref(frame);
×
1656
    return nret;
×
1657
}
1658

1659

1660
static int
1661
test2(void)
×
1662
{
1663
    sixel_frame_t *frame = NULL;
×
1664
    int nret = EXIT_FAILURE;
×
1665
    unsigned char *pixels = malloc(4);
×
1666
    unsigned char *bgcolor = malloc(3);
×
1667
    unsigned char *u8pixels;
×
1668
    SIXELSTATUS status;
×
1669

1670
    pixels[0] = 0x43;
×
1671
    pixels[1] = 0x89;
×
1672
    pixels[2] = 0x97;
×
1673
    pixels[3] = 0x32;
×
1674

1675
    memset(bgcolor, 0x10, 3);
×
1676

1677
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1678
#  pragma GCC diagnostic push
1679
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1680
#endif
1681
    frame = sixel_frame_create();
×
1682
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1683
#  pragma GCC diagnostic pop
1684
#endif
1685

1686
    if (frame == NULL) {
×
1687
        goto error;
×
1688
    }
1689

1690
    status = sixel_frame_init(frame,
×
1691
                              pixels,
1692
                              1,
1693
                              1,
1694
                              SIXEL_PIXELFORMAT_RGBA8888,
1695
                              NULL,
1696
                              0);
1697
    if (SIXEL_FAILED(status)) {
×
1698
        goto error;
×
1699
    }
1700

1701
    status = sixel_frame_strip_alpha(frame, bgcolor);
×
1702
    if (SIXEL_FAILED(status)) {
×
1703
        goto error;
1704
    }
1705

1706
    if (frame->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1707
        goto error;
×
1708
    }
1709

1710
    u8pixels = frame->pixels.u8ptr;
×
1711

1712
    if (u8pixels[0] != (0x43 * 0x32 + 0x10 * (0xff - 0x32)) >> 8) {
×
1713
        goto error;
×
1714
    }
1715

1716
    if (u8pixels[1] != (0x89 * 0x32 + 0x10 * (0xff - 0x32)) >> 8) {
×
1717
        goto error;
×
1718
    }
1719

1720
    if (u8pixels[2] != (0x97 * 0x32 + 0x10 * (0xff - 0x32)) >> 8) {
×
1721
        goto error;
×
1722
    }
1723

1724
    nret = EXIT_SUCCESS;
1725

1726
error:
1727
    sixel_frame_unref(frame);
×
1728
    return nret;
×
1729
}
1730

1731

1732
static int
1733
test3(void)
×
1734
{
1735
    sixel_frame_t *frame = NULL;
×
1736
    int nret = EXIT_FAILURE;
×
1737
    unsigned char *pixels = malloc(4);
×
1738
    unsigned char *u8pixels;
×
1739
    SIXELSTATUS status;
×
1740

1741
    pixels[0] = 0x43;
×
1742
    pixels[1] = 0x89;
×
1743
    pixels[2] = 0x97;
×
1744
    pixels[3] = 0x32;
×
1745

1746
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1747
#  pragma GCC diagnostic push
1748
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1749
#endif
1750
    frame = sixel_frame_create();
×
1751
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1752
#  pragma GCC diagnostic pop
1753
#endif
1754
    if (frame == NULL) {
×
1755
        goto error;
×
1756
    }
1757

1758
    status = sixel_frame_init(frame,
×
1759
                              pixels,
1760
                              1,
1761
                              1,
1762
                              SIXEL_PIXELFORMAT_RGBA8888,
1763
                              NULL,
1764
                              0);
1765
    if (SIXEL_FAILED(status)) {
×
1766
        goto error;
×
1767
    }
1768

1769
    status = sixel_frame_strip_alpha(frame, NULL);
×
1770
    if (SIXEL_FAILED(status)) {
×
1771
        goto error;
1772
    }
1773

1774
    if (frame->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1775
        goto error;
×
1776
    }
1777

1778
    u8pixels = frame->pixels.u8ptr;
×
1779

1780
    if (u8pixels[0] != 0x43) {
×
1781
        goto error;
×
1782
    }
1783

1784
    if (u8pixels[1] != 0x89) {
×
1785
        goto error;
×
1786
    }
1787

1788
    if (u8pixels[2] != 0x97) {
×
1789
        goto error;
×
1790
    }
1791

1792
    nret = EXIT_SUCCESS;
1793

1794
error:
1795
    sixel_frame_unref(frame);
×
1796
    return nret;
×
1797
}
1798

1799

1800
static int
1801
test4(void)
×
1802
{
1803
    sixel_frame_t *frame = NULL;
×
1804
    int nret = EXIT_FAILURE;
×
1805
    unsigned char *pixels = malloc(4);
×
1806
    unsigned char *u8pixels;
×
1807
    SIXELSTATUS status;
×
1808

1809
    pixels[0] = 0x43;
×
1810
    pixels[1] = 0x89;
×
1811
    pixels[2] = 0x97;
×
1812
    pixels[3] = 0x32;
×
1813

1814
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1815
#  pragma GCC diagnostic push
1816
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1817
#endif
1818
    frame = sixel_frame_create();
×
1819
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1820
#  pragma GCC diagnostic pop
1821
#endif
1822
    if (frame == NULL) {
×
1823
        goto error;
×
1824
    }
1825

1826
    status = sixel_frame_init(frame,
×
1827
                              pixels,
1828
                              1,
1829
                              1,
1830
                              SIXEL_PIXELFORMAT_ARGB8888,
1831
                              NULL,
1832
                              0);
1833
    if (SIXEL_FAILED(status)) {
×
1834
        goto error;
×
1835
    }
1836

1837
    status = sixel_frame_strip_alpha(frame, NULL);
×
1838
    if (SIXEL_FAILED(status)) {
×
1839
        goto error;
1840
    }
1841

1842
    if (frame->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1843
        goto error;
×
1844
    }
1845

1846
    u8pixels = frame->pixels.u8ptr;
×
1847

1848
    if (u8pixels[0] != 0x89) {
×
1849
        goto error;
×
1850
    }
1851

1852
    if (u8pixels[1] != 0x97) {
×
1853
        goto error;
×
1854
    }
1855

1856
    if (u8pixels[2] != 0x32) {
×
1857
        goto error;
×
1858
    }
1859

1860
    nret = EXIT_SUCCESS;
1861

1862
error:
1863
    sixel_frame_unref(frame);
×
1864
    return nret;
×
1865
}
1866

1867

1868
static int
1869
test5(void)
×
1870
{
1871
    sixel_frame_t *frame = NULL;
×
1872
    int nret = EXIT_FAILURE;
×
1873
    unsigned char *pixels = malloc(1);
×
1874
    unsigned char *palette = malloc(3);
×
1875
    unsigned char *u8pixels;
×
1876
    SIXELSTATUS status;
×
1877

1878
    palette[0] = 0x43;
×
1879
    palette[1] = 0x89;
×
1880
    palette[2] = 0x97;
×
1881

1882
    pixels[0] = 0;
×
1883

1884
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1885
#  pragma GCC diagnostic push
1886
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1887
#endif
1888
    frame = sixel_frame_create();
×
1889
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1890
#  pragma GCC diagnostic pop
1891
#endif
1892
    if (frame == NULL) {
×
1893
        goto error;
×
1894
    }
1895

1896
    status = sixel_frame_init(frame,
×
1897
                              pixels,
1898
                              1,
1899
                              1,
1900
                              SIXEL_PIXELFORMAT_PAL8,
1901
                              palette,
1902
                              1);
1903
    if (SIXEL_FAILED(status)) {
×
1904
        goto error;
×
1905
    }
1906

1907
    status = sixel_frame_convert_to_rgb888(frame);
×
1908
    if (SIXEL_FAILED(status)) {
×
1909
        goto error;
×
1910
    }
1911

1912
    if (frame->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1913
        goto error;
×
1914
    }
1915

1916
    u8pixels = frame->pixels.u8ptr;
×
1917

1918
    if (u8pixels[0] != 0x43) {
×
1919
        goto error;
×
1920
    }
1921

1922
    if (u8pixels[1] != 0x89) {
×
1923
        goto error;
×
1924
    }
1925

1926
    if (u8pixels[2] != 0x97) {
×
1927
        goto error;
×
1928
    }
1929

1930
    nret = EXIT_SUCCESS;
1931

1932
error:
1933
    sixel_frame_unref(frame);
×
1934
    return nret;
×
1935
}
1936

1937

1938
static int
1939
test6(void)
×
1940
{
1941
    sixel_frame_t *frame = NULL;
×
1942
    int nret = EXIT_FAILURE;
×
1943
    unsigned char *pixels = malloc(6);
×
1944
    unsigned char *palette = malloc(3);
×
1945
    unsigned char *u8pixels;
×
1946
    SIXELSTATUS status;
×
1947

1948
    palette[0] = 0x43;
×
1949
    palette[1] = 0x89;
×
1950
    palette[2] = 0x97;
×
1951

1952
    pixels[0] = 0;
×
1953

1954
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1955
#  pragma GCC diagnostic push
1956
#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1957
#endif
1958
    frame = sixel_frame_create();
×
1959
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
1960
#  pragma GCC diagnostic pop
1961
#endif
1962
    if (frame == NULL) {
×
1963
        goto error;
×
1964
    }
1965

1966
    status = sixel_frame_init(frame,
×
1967
                              pixels,
1968
                              1,
1969
                              1,
1970
                              SIXEL_PIXELFORMAT_PAL1,
1971
                              palette,
1972
                              1);
1973
    if (SIXEL_FAILED(status)) {
×
1974
        goto error;
×
1975
    }
1976

1977
    status = sixel_frame_convert_to_rgb888(frame);
×
1978
    if (SIXEL_FAILED(status)) {
×
1979
        goto error;
×
1980
    }
1981

1982
    if (frame->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
×
1983
        goto error;
×
1984
    }
1985

1986
    u8pixels = frame->pixels.u8ptr;
×
1987

1988
    if (u8pixels[0] != 0x43) {
×
1989
        goto error;
×
1990
    }
1991

1992
    if (u8pixels[1] != 0x89) {
×
1993
        goto error;
×
1994
    }
1995

1996
    if (u8pixels[2] != 0x97) {
×
1997
        goto error;
×
1998
    }
1999

2000
    nret = EXIT_SUCCESS;
2001

2002
error:
2003
    sixel_frame_unref(frame);
×
2004
    return nret;
×
2005
}
2006

2007

2008
SIXELAPI int
2009
sixel_frame_tests_main(void)
×
2010
{
2011
    int nret = EXIT_FAILURE;
×
2012
    size_t i;
×
2013
    typedef int (* testcase)(void);
2014

2015
    static testcase const testcases[] = {
2016
        test1,
2017
        test2,
2018
        test3,
2019
        test4,
2020
        test5,
2021
        test6,
2022
    };
2023

2024
    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
×
2025
        nret = testcases[i]();
×
2026
        if (nret != EXIT_SUCCESS) {
×
2027
            goto error;
×
2028
        }
2029
    }
2030

2031
    nret = EXIT_SUCCESS;
2032

2033
error:
2034
    return nret;
×
2035
}
2036
#endif  /* HAVE_TESTS */
2037

2038
/* emacs Local Variables:      */
2039
/* emacs mode: c               */
2040
/* emacs tab-width: 4          */
2041
/* emacs indent-tabs-mode: nil */
2042
/* emacs c-basic-offset: 4     */
2043
/* emacs End:                  */
2044
/* vim: set expandtab ts=4 sts=4 sw=4 : */
2045
/* 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