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

saitoha / libsixel / 19389365033

15 Nov 2025 11:44AM UTC coverage: 43.379% (-0.4%) from 43.821%
19389365033

push

github

saitoha
palette: refactor palette helpers into dedicated modules

8474 of 27744 branches covered (30.54%)

44 of 650 new or added lines in 4 files covered. (6.77%)

106 existing lines in 9 files now uncovered.

11581 of 26697 relevant lines covered (43.38%)

973309.3 hits per line

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

28.12
/src/lut.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * mediancut algorithm implementation is imported from pnmcolormap.c
5
 * in netpbm library.
6
 * http://netpbm.sourceforge.net/
7
 *
8
 * *******************************************************************************
9
 *                  original license block of pnmcolormap.c
10
 * *******************************************************************************
11
 *
12
 *   Derived from ppmquant, originally by Jef Poskanzer.
13
 *
14
 *   Copyright (C) 1989, 1991 by Jef Poskanzer.
15
 *   Copyright (C) 2001 by Bryan Henderson.
16
 *
17
 *   Permission to use, copy, modify, and distribute this software and its
18
 *   documentation for any purpose and without fee is hereby granted, provided
19
 *   that the above copyright notice appear in all copies and that both that
20
 *   copyright notice and this permission notice appear in supporting
21
 *   documentation.  This software is provided "as is" without express or
22
 *   implied warranty.
23
 *
24
 * ******************************************************************************
25
 *
26
 * Copyright (c) 2014-2018 Hayaki Saito
27
 *
28
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
29
 * this software and associated documentation files (the "Software"), to deal in
30
 * the Software without restriction, including without limitation the rights to
31
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
32
 * the Software, and to permit persons to whom the Software is furnished to do so,
33
 * subject to the following conditions:
34
 *
35
 * The above copyright notice and this permission notice shall be included in all
36
 * copies or substantial portions of the Software.
37
 *
38
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
40
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
41
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
42
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44
 */
45

46
/* The lookup table implementation manages colour quantization caches and the
47
 * CERT LUT accelerator.  Median-cut specific histogram routines now reside in
48
 * palette-heckbert.c so this file can concentrate on lookup responsibilities.
49
 */
50

51
#include "config.h"
52

53
#include <limits.h>
54
#include <stdint.h>
55
#include <stdio.h>
56
#include <stdlib.h>
57
#include <string.h>
58
#if defined(_MSC_VER) && defined(HAVE_INTRIN_H)
59
#include <intrin.h>
60
#endif
61

62
#include <sixel.h>
63

64
#include "status.h"
65
#include "compat_stub.h"
66
#include "allocator.h"
67
#include "lut.h"
68

69
#define SIXEL_LUT_BRANCH_FLAG 0x40000000U
70
/* #define DEBUG_LUT_TRACE 1 */
71

72
typedef struct sixel_certlut_color {
73
    uint8_t r;
74
    uint8_t g;
75
    uint8_t b;
76
} sixel_certlut_color_t;
77

78
typedef struct sixel_certlut_node {
79
    int index;
80
    int left;
81
    int right;
82
    unsigned char axis;
83
} sixel_certlut_node_t;
84

85
typedef struct sixel_certlut {
86
    uint32_t *level0;
87
    uint8_t *pool;
88
    uint32_t pool_size;
89
    uint32_t pool_capacity;
90
    int wR;
91
    int wG;
92
    int wB;
93
    uint64_t wR2;
94
    uint64_t wG2;
95
    uint64_t wB2;
96
    int32_t wr_scale[256];
97
    int32_t wg_scale[256];
98
    int32_t wb_scale[256];
99
    int32_t *wr_palette;
100
    int32_t *wg_palette;
101
    int32_t *wb_palette;
102
    sixel_certlut_color_t const *palette;
103
    int ncolors;
104
    sixel_certlut_node_t *kdnodes;
105
    int kdnodes_count;
106
    int kdtree_root;
107
} sixel_certlut_t;
108

109
typedef struct sixel_lut_quantization {
110
    unsigned int channel_shift;
111
    unsigned int channel_bits;
112
    unsigned int channel_mask;
113
} sixel_lut_quantization_t;
114

115
struct sixel_lut {
116
    int policy;
117
    int depth;
118
    int ncolors;
119
    int complexion;
120
    unsigned char const *palette;
121
    sixel_allocator_t *allocator;
122
    sixel_lut_quantization_t quant;
123
    int32_t *dense;
124
    size_t dense_size;
125
    int dense_ready;
126
    sixel_certlut_t cert;
127
    int cert_ready;
128
};
129

130
/* Sentinel value used to detect empty dense LUT slots. */
131
#define SIXEL_LUT_DENSE_EMPTY (-1)
132

133
/*
134
 * Compute quantization parameters for the dense cache.  The selection mirrors
135
 * the historic histogram helper so existing 5bit/6bit behaviour stays intact
136
 * while still allowing "none" and "certlut" to bypass the cache entirely.
137
 */
138
static sixel_lut_quantization_t
139
sixel_lut_quant_make(unsigned int depth, int policy)
316✔
140
{
141
    sixel_lut_quantization_t quant;
142
    unsigned int shift;
143

144
    shift = 2U;
316✔
145
    if (depth > 3U) {
316!
NEW
146
        shift = 3U;
×
147
    }
148
    if (policy == SIXEL_LUT_POLICY_5BIT) {
316!
NEW
149
        shift = 3U;
×
150
    } else if (policy == SIXEL_LUT_POLICY_6BIT) {
316!
151
        shift = 2U;
316✔
152
        if (depth > 3U) {
316!
NEW
153
            shift = 3U;
×
154
        }
NEW
155
    } else if (policy == SIXEL_LUT_POLICY_NONE
×
NEW
156
               || policy == SIXEL_LUT_POLICY_CERTLUT) {
×
NEW
157
        shift = 0U;
×
158
    }
159

160
    quant.channel_shift = shift;
316✔
161
    quant.channel_bits = 8U - shift;
316✔
162
    quant.channel_mask = (1U << quant.channel_bits) - 1U;
316✔
163

164
    return quant;
316✔
165
}
166

167
/*
168
 * Return the dense table size for the active quantization.  The loop saturates
169
 * at SIZE_MAX so the later allocation guard can emit a friendly error message
170
 * instead of overflowing.
171
 */
172
static size_t
173
sixel_lut_dense_size(unsigned int depth,
316✔
174
                     sixel_lut_quantization_t const *quant)
175
{
176
    size_t size;
177
    unsigned int exponent;
178
    unsigned int i;
179

180
    size = 1U;
316✔
181
    exponent = depth * quant->channel_bits;
316✔
182
    for (i = 0U; i < exponent; ++i) {
6,004✔
183
        if (size > SIZE_MAX / 2U) {
5,688!
NEW
184
            size = SIZE_MAX;
×
NEW
185
            break;
×
186
        }
187
        size <<= 1U;
5,688✔
188
    }
189

190
    return size;
316✔
191
}
192

193
/*
194
 * Pack a pixel into the dense cache index.  The rounding matches the old
195
 * histogram_pack_color() implementation to keep cached answers stable.
196
 */
197
static unsigned int
198
sixel_lut_pack_color(unsigned char const *pixel,
21,098,740✔
199
                     unsigned int depth,
200
                     sixel_lut_quantization_t const *quant)
201
{
202
    unsigned int packed;
203
    unsigned int bits;
204
    unsigned int shift;
205
    unsigned int plane;
206
    unsigned int component;
207
    unsigned int rounded;
208
    unsigned int mask;
209

210
    packed = 0U;
21,098,740✔
211
    bits = quant->channel_bits;
21,098,740✔
212
    shift = quant->channel_shift;
21,098,740✔
213
    mask = quant->channel_mask;
21,098,740✔
214

215
    for (plane = 0U; plane < depth; ++plane) {
84,394,960✔
216
        component = (unsigned int)pixel[depth - 1U - plane];
63,296,220✔
217
        if (shift > 0U) {
63,296,220!
218
            rounded = (component + (1U << (shift - 1U))) >> shift;
63,296,220✔
219
            if (rounded > mask) {
63,296,220✔
220
                rounded = mask;
1,691,072✔
221
            }
222
        } else {
NEW
223
            rounded = component & mask;
×
224
        }
225
        packed |= rounded << (plane * bits);
63,296,220✔
226
    }
227

228
    return packed;
21,098,740✔
229
}
230

231
static int
232
sixel_lut_policy_normalize(int policy)
472✔
233
{
234
    int normalized;
235

236
    normalized = policy;
472✔
237
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
472!
238
        normalized = SIXEL_LUT_POLICY_6BIT;
×
239
    } else if (normalized != SIXEL_LUT_POLICY_5BIT
472!
240
               && normalized != SIXEL_LUT_POLICY_6BIT
472!
UNCOV
241
               && normalized != SIXEL_LUT_POLICY_CERTLUT
×
242
               && normalized != SIXEL_LUT_POLICY_NONE) {
×
243
        normalized = SIXEL_LUT_POLICY_6BIT;
×
244
    }
245

246
    return normalized;
472✔
247
}
248

249
static int
250
sixel_lut_policy_uses_cache(int policy)
632✔
251
{
252
    if (policy == SIXEL_LUT_POLICY_CERTLUT
632!
253
        || policy == SIXEL_LUT_POLICY_NONE) {
632!
254
        return 0;
×
255
    }
256

257
    return 1;
632✔
258
}
259

260
static void
261
sixel_lut_release_cache(sixel_lut_t *lut)
156✔
262
{
263
    if (lut == NULL || lut->dense == NULL) {
156!
264
        return;
×
265
    }
266

267
    sixel_allocator_free(lut->allocator, lut->dense);
156✔
268
    lut->dense = NULL;
156✔
269
    lut->dense_size = 0U;
156✔
270
    lut->dense_ready = 0;
156✔
271
}
272

273
static SIXELSTATUS
274
sixel_lut_prepare_cache(sixel_lut_t *lut)
316✔
275
{
276
    size_t expected;
277
    size_t bytes;
278
    size_t index;
279

280
    if (lut == NULL) {
316!
281
        return SIXEL_BAD_ARGUMENT;
×
282
    }
283
    if (!sixel_lut_policy_uses_cache(lut->policy)) {
316!
284
        return SIXEL_OK;
×
285
    }
286
    if (lut->allocator == NULL) {
316!
287
        return SIXEL_BAD_ARGUMENT;
×
288
    }
289

290
    /* Allocate a dense RGB quantization table for 5/6bit policies.
291
     * The packed index matches sixel_lut_pack_color() so each slot
292
     * can store the resolved palette entry or remain empty.
293
     */
294
    expected = sixel_lut_dense_size((unsigned int)lut->depth,
316✔
295
                                    &lut->quant);
316✔
296
    if (expected == 0U) {
316!
297
        return SIXEL_BAD_ARGUMENT;
×
298
    }
299
    if (expected > SIZE_MAX / sizeof(int32_t)) {
316!
300
        sixel_helper_set_additional_message(
×
301
            "sixel_lut_prepare_cache: dense table too large.");
302
        return SIXEL_BAD_ALLOCATION;
×
303
    }
304

305
    if (lut->dense != NULL && lut->dense_size != expected) {
316!
306
        sixel_lut_release_cache(lut);
×
307
    }
308

309
    if (lut->dense == NULL) {
316✔
310
        bytes = expected * sizeof(int32_t);
156✔
311
        lut->dense = (int32_t *)sixel_allocator_malloc(lut->allocator,
156✔
312
                                                       bytes);
313
        if (lut->dense == NULL) {
156!
314
            sixel_helper_set_additional_message(
×
315
                "sixel_lut_prepare_cache: allocation failed.");
316
            return SIXEL_BAD_ALLOCATION;
×
317
        }
318
    }
319

320
    for (index = 0U; index < expected; ++index) {
82,837,820✔
321
        lut->dense[index] = SIXEL_LUT_DENSE_EMPTY;
82,837,504✔
322
    }
323

324
    lut->dense_size = expected;
316✔
325
    lut->dense_ready = 1;
316✔
326

327
    return SIXEL_OK;
316✔
328
}
329

330
static int
331
sixel_lut_lookup_fast(sixel_lut_t *lut, unsigned char const *pixel)
21,098,740✔
332
{
333
    int result;
334
    int diff;
335
    int i;
336
    int distant;
337
    unsigned char const *entry;
338
    unsigned char const *end;
339
    int pixel0;
340
    int pixel1;
341
    int pixel2;
342
    int delta;
343
    unsigned int bucket;
344
    int32_t cached;
345

346
    if (lut == NULL || pixel == NULL) {
21,098,740!
347
        return 0;
×
348
    }
349
    if (lut->palette == NULL || lut->ncolors <= 0) {
21,098,740!
350
        return 0;
×
351
    }
352

353
    bucket = 0U;
21,098,740✔
354
    cached = SIXEL_LUT_DENSE_EMPTY;
21,098,740✔
355
    if (lut->dense_ready && lut->dense != NULL) {
21,098,740!
356
        bucket = sixel_lut_pack_color(pixel,
21,098,740✔
357
                                      (unsigned int)lut->depth,
21,098,740✔
358
                                      &lut->quant);
21,098,740✔
359
        if ((size_t)bucket < lut->dense_size) {
21,098,740!
360
            cached = lut->dense[bucket];
21,098,740✔
361
            if (cached >= 0) {
21,098,740✔
362
                return cached;
19,963,864✔
363
            }
364
        }
365
    }
366

367
    result = (-1);
1,134,876✔
368
    diff = INT_MAX;
1,134,876✔
369
    /* Linear palette scan remains as a safety net when the dense
370
     * lookup does not have an answer yet.
371
     */
372
    entry = lut->palette;
1,134,876✔
373
    end = lut->palette + (size_t)lut->ncolors * (size_t)lut->depth;
1,134,876✔
374
    pixel0 = (int)pixel[0];
1,134,876✔
375
    pixel1 = (int)pixel[1];
1,134,876✔
376
    pixel2 = (int)pixel[2];
1,134,876✔
377
    for (i = 0; entry < end; ++i, entry += lut->depth) {
202,121,322✔
378
        delta = pixel0 - (int)entry[0];
200,986,446✔
379
        distant = delta * delta * lut->complexion;
200,986,446✔
380
        delta = pixel1 - (int)entry[1];
200,986,446✔
381
        distant += delta * delta;
200,986,446✔
382
        delta = pixel2 - (int)entry[2];
200,986,446✔
383
        distant += delta * delta;
200,986,446✔
384
        if (distant < diff) {
200,986,446✔
385
            diff = distant;
8,559,134✔
386
            result = i;
8,559,134✔
387
        }
388
    }
389

390
    if (lut->dense_ready && lut->dense != NULL && result >= 0) {
1,134,876!
391
        if ((size_t)bucket < lut->dense_size) {
1,134,876!
392
            lut->dense[bucket] = result;
1,134,876✔
393
        }
394
    }
395

396
    if (result < 0) {
1,134,876!
397
        result = 0;
×
398
    }
399

400
    return result;
1,134,876✔
401
}
402

403
static int
404
sixel_certlut_init(sixel_certlut_t *lut)
156✔
405
{
406
    int status;
407

408
    status = SIXEL_FALSE;
156✔
409
    if (lut == NULL) {
156!
410
        goto end;
×
411
    }
412

413
    lut->level0 = NULL;
156✔
414
    lut->pool = NULL;
156✔
415
    lut->pool_size = 0U;
156✔
416
    lut->pool_capacity = 0U;
156✔
417
    lut->wR = 1;
156✔
418
    lut->wG = 1;
156✔
419
    lut->wB = 1;
156✔
420
    lut->wR2 = 1U;
156✔
421
    lut->wG2 = 1U;
156✔
422
    lut->wB2 = 1U;
156✔
423
    memset(lut->wr_scale, 0, sizeof(lut->wr_scale));
156✔
424
    memset(lut->wg_scale, 0, sizeof(lut->wg_scale));
156✔
425
    memset(lut->wb_scale, 0, sizeof(lut->wb_scale));
156✔
426
    lut->wr_palette = NULL;
156✔
427
    lut->wg_palette = NULL;
156✔
428
    lut->wb_palette = NULL;
156✔
429
    lut->palette = NULL;
156✔
430
    lut->ncolors = 0;
156✔
431
    lut->kdnodes = NULL;
156✔
432
    lut->kdnodes_count = 0;
156✔
433
    lut->kdtree_root = -1;
156✔
434
    status = SIXEL_OK;
156✔
435

436
end:
156✔
437
    return status;
156✔
438
}
439

440
static void
441
sixel_certlut_release(sixel_certlut_t *lut)
×
442
{
443
    if (lut == NULL) {
×
444
        return;
×
445
    }
446
    free(lut->level0);
×
447
    free(lut->pool);
×
448
    free(lut->wr_palette);
×
449
    free(lut->wg_palette);
×
450
    free(lut->wb_palette);
×
451
    free(lut->kdnodes);
×
452
    lut->level0 = NULL;
×
453
    lut->pool = NULL;
×
454
    lut->pool_size = 0U;
×
455
    lut->pool_capacity = 0U;
×
456
    lut->wr_palette = NULL;
×
457
    lut->wg_palette = NULL;
×
458
    lut->wb_palette = NULL;
×
459
    lut->kdnodes = NULL;
×
460
    lut->kdnodes_count = 0;
×
461
    lut->kdtree_root = -1;
×
462
}
463

464
static int
465
sixel_certlut_prepare_palette_terms(sixel_certlut_t *lut)
×
466
{
467
    int status;
468
    size_t count;
469
    int index;
470
    int32_t *wr_terms;
471
    int32_t *wg_terms;
472
    int32_t *wb_terms;
473

474
    status = SIXEL_FALSE;
×
475
    wr_terms = NULL;
×
476
    wg_terms = NULL;
×
477
    wb_terms = NULL;
×
478
    if (lut == NULL) {
×
479
        goto end;
×
480
    }
481
    if (lut->ncolors <= 0) {
×
482
        status = SIXEL_OK;
×
483
        goto end;
×
484
    }
485
    count = (size_t)lut->ncolors;
×
486
    wr_terms = (int32_t *)malloc(count * sizeof(int32_t));
×
487
    if (wr_terms == NULL) {
×
488
        goto end;
×
489
    }
490
    wg_terms = (int32_t *)malloc(count * sizeof(int32_t));
×
491
    if (wg_terms == NULL) {
×
492
        goto end;
×
493
    }
494
    wb_terms = (int32_t *)malloc(count * sizeof(int32_t));
×
495
    if (wb_terms == NULL) {
×
496
        goto end;
×
497
    }
498
    for (index = 0; index < lut->ncolors; ++index) {
×
499
        wr_terms[index] = lut->wR * (int)lut->palette[index].r;
×
500
        wg_terms[index] = lut->wG * (int)lut->palette[index].g;
×
501
        wb_terms[index] = lut->wB * (int)lut->palette[index].b;
×
502
    }
503
    free(lut->wr_palette);
×
504
    free(lut->wg_palette);
×
505
    free(lut->wb_palette);
×
506
    lut->wr_palette = wr_terms;
×
507
    lut->wg_palette = wg_terms;
×
508
    lut->wb_palette = wb_terms;
×
509
    wr_terms = NULL;
×
510
    wg_terms = NULL;
×
511
    wb_terms = NULL;
×
512
    status = SIXEL_OK;
×
513

UNCOV
514
end:
×
515
    free(wr_terms);
×
516
    free(wg_terms);
×
517
    free(wb_terms);
×
518
    return status;
×
519
}
520

521
static void
522
sixel_certlut_cell_center(int rmin, int gmin, int bmin, int size,
×
523
                        int *cr, int *cg, int *cb)
524
{
525
    int half;
526

527
    half = size / 2;
×
528
    *cr = rmin + half;
×
529
    *cg = gmin + half;
×
530
    *cb = bmin + half;
×
531
    if (size == 1) {
×
532
        *cr = rmin;
×
533
        *cg = gmin;
×
534
        *cb = bmin;
×
535
    }
536
}
×
537

538
static void
539
sixel_certlut_weight_init(sixel_certlut_t *lut, int wR, int wG, int wB)
×
540
{
541
    int i;
542

543
    lut->wR = wR;
×
544
    lut->wG = wG;
×
545
    lut->wB = wB;
×
546
    lut->wR2 = (uint64_t)wR * (uint64_t)wR;
×
547
    lut->wG2 = (uint64_t)wG * (uint64_t)wG;
×
548
    lut->wB2 = (uint64_t)wB * (uint64_t)wB;
×
549
    for (i = 0; i < 256; ++i) {
×
550
        lut->wr_scale[i] = wR * i;
×
551
        lut->wg_scale[i] = wG * i;
×
552
        lut->wb_scale[i] = wB * i;
×
553
    }
554
}
×
555

556
static uint64_t
557
sixel_certlut_distance_precomputed(sixel_certlut_t const *lut,
×
558
                                   int index,
559
                                   int32_t wr_r,
560
                                   int32_t wg_g,
561
                                   int32_t wb_b)
562
{
563
    uint64_t distance;
564
    int64_t diff;
565

566
    diff = (int64_t)wr_r - (int64_t)lut->wr_palette[index];
×
567
    distance = (uint64_t)(diff * diff);
×
568
    diff = (int64_t)wg_g - (int64_t)lut->wg_palette[index];
×
569
    distance += (uint64_t)(diff * diff);
×
570
    diff = (int64_t)wb_b - (int64_t)lut->wb_palette[index];
×
571
    distance += (uint64_t)(diff * diff);
×
572

573
    return distance;
×
574
}
575

576
static int
577
sixel_certlut_is_cell_safe(sixel_certlut_t const *lut, int best_idx,
×
578
                         int second_idx, int size, uint64_t best_dist,
579
                         uint64_t second_dist)
580
{
581
    uint64_t delta_sq;
582
    uint64_t rhs;
583
    uint64_t weight_term;
584
    int64_t wr_delta;
585
    int64_t wg_delta;
586
    int64_t wb_delta;
587

588
    if (best_idx < 0 || second_idx < 0) {
×
589
        return 1;
×
590
    }
591

592
    /*
593
     * The certification bound compares the squared distance gap against the
594
     * palette separation scaled by the cube diameter.  If the gap wins the
595
     * entire cube maps to the current best palette entry.
596
     */
597
    delta_sq = second_dist - best_dist;
×
598
    wr_delta = (int64_t)lut->wr_palette[second_idx]
×
599
        - (int64_t)lut->wr_palette[best_idx];
×
600
    wg_delta = (int64_t)lut->wg_palette[second_idx]
×
601
        - (int64_t)lut->wg_palette[best_idx];
×
602
    wb_delta = (int64_t)lut->wb_palette[second_idx]
×
603
        - (int64_t)lut->wb_palette[best_idx];
×
604
    weight_term = (uint64_t)(wr_delta * wr_delta);
×
605
    weight_term += (uint64_t)(wg_delta * wg_delta);
×
606
    weight_term += (uint64_t)(wb_delta * wb_delta);
×
607
    rhs = (uint64_t)3 * (uint64_t)size * (uint64_t)size * weight_term;
×
608

609
    return delta_sq * delta_sq > rhs;
×
610
}
611

612
static uint32_t
613
sixel_certlut_pool_alloc(sixel_certlut_t *lut, int *status)
×
614
{
615
    uint32_t required;
616
    uint32_t next_capacity;
617
    uint32_t offset;
618
    uint8_t *resized;
619

620
    offset = 0U;
×
621
    if (status != NULL) {
×
622
        *status = SIXEL_FALSE;
×
623
    }
624
    required = lut->pool_size + (uint32_t)(8 * sizeof(uint32_t));
×
625
    if (required > lut->pool_capacity) {
×
626
        next_capacity = lut->pool_capacity;
×
627
        if (next_capacity == 0U) {
×
628
            next_capacity = (uint32_t)(8 * sizeof(uint32_t));
×
629
        }
630
        while (next_capacity < required) {
×
631
            if (next_capacity > UINT32_MAX / 2U) {
×
632
                return 0U;
×
633
            }
634
            next_capacity *= 2U;
×
635
        }
636
        resized = (uint8_t *)realloc(lut->pool, next_capacity);
×
637
        if (resized == NULL) {
×
638
            return 0U;
×
639
        }
640
        lut->pool = resized;
×
641
        lut->pool_capacity = next_capacity;
×
642
    }
643
    offset = lut->pool_size;
×
644
    memset(lut->pool + offset, 0, 8 * sizeof(uint32_t));
×
645
    lut->pool_size = required;
×
646
    if (status != NULL) {
×
647
        *status = SIXEL_OK;
×
648
    }
649

650
    return offset;
×
651
}
652

653
static void
654
sixel_certlut_assign_leaf(uint32_t *cell, int palette_index)
×
655
{
656
    *cell = 0x80000000U | (uint32_t)(palette_index & 0xff);
×
657
}
×
658

659
static void
660
sixel_certlut_assign_branch(uint32_t *cell, uint32_t offset)
×
661
{
662
    *cell = SIXEL_LUT_BRANCH_FLAG | (offset & 0x3fffffffU);
×
663
}
×
664

665
static int
666
sixel_certlut_palette_component(sixel_certlut_t const *lut,
×
667
                                int index, int axis)
668
{
669
    sixel_certlut_color_t const *color;
670

671
    color = &lut->palette[index];
×
672
    if (axis == 0) {
×
673
        return (int)color->r;
×
674
    }
675
    if (axis == 1) {
×
676
        return (int)color->g;
×
677
    }
678
    return (int)color->b;
×
679
}
680

681
static void
682
sixel_certlut_sort_indices(sixel_certlut_t const *lut,
×
683
                           int *indices, int count, int axis)
684
{
685
    int i;
686
    int j;
687
    int key;
688
    int key_value;
689
    int current_value;
690

691
    for (i = 1; i < count; ++i) {
×
692
        key = indices[i];
×
693
        key_value = sixel_certlut_palette_component(lut, key, axis);
×
694
        j = i - 1;
×
695
        while (j >= 0) {
×
696
            current_value = sixel_certlut_palette_component(lut,
×
697
                                                            indices[j],
×
698
                                                            axis);
699
            if (current_value <= key_value) {
×
700
                break;
×
701
            }
702
            indices[j + 1] = indices[j];
×
703
            --j;
×
704
        }
705
        indices[j + 1] = key;
×
706
    }
707
}
×
708

709
static int
710
sixel_certlut_kdtree_build_recursive(sixel_certlut_t *lut,
×
711
                                     int *indices,
712
                                     int count,
713
                                     int depth)
714
{
715
    int axis;
716
    int median;
717
    int node_index;
718

719
    if (count <= 0) {
×
720
        return -1;
×
721
    }
722

723
    axis = depth % 3;
×
724
    sixel_certlut_sort_indices(lut, indices, count, axis);
×
725
    median = count / 2;
×
726
    node_index = lut->kdnodes_count;
×
727
    if (node_index >= lut->ncolors) {
×
728
        return -1;
×
729
    }
730
    lut->kdnodes_count++;
×
731
    lut->kdnodes[node_index].index = indices[median];
×
732
    lut->kdnodes[node_index].axis = (unsigned char)axis;
×
733
    lut->kdnodes[node_index].left =
×
734
        sixel_certlut_kdtree_build_recursive(lut,
×
735
                                             indices,
736
                                             median,
737
                                             depth + 1);
738
    lut->kdnodes[node_index].right =
×
739
        sixel_certlut_kdtree_build_recursive(lut,
×
740
                                             indices + median + 1,
×
741
                                             count - median - 1,
×
742
                                             depth + 1);
743

744
    return node_index;
×
745
}
746

747
static SIXELSTATUS
748
sixel_certlut_kdtree_build(sixel_certlut_t *lut)
×
749
{
750
    SIXELSTATUS status;
751
    int *indices;
752
    int i;
753

754
    status = SIXEL_FALSE;
×
755
    indices = NULL;
×
756
    lut->kdnodes = NULL;
×
757
    lut->kdnodes_count = 0;
×
758
    lut->kdtree_root = -1;
×
759
    if (lut->ncolors <= 0) {
×
760
        status = SIXEL_OK;
×
761
        goto end;
×
762
    }
763
    lut->kdnodes = (sixel_certlut_node_t *)
×
764
        calloc((size_t)lut->ncolors, sizeof(sixel_certlut_node_t));
×
765
    if (lut->kdnodes == NULL) {
×
766
        goto end;
×
767
    }
768
    indices = (int *)malloc((size_t)lut->ncolors * sizeof(int));
×
769
    if (indices == NULL) {
×
770
        goto end;
×
771
    }
772
    for (i = 0; i < lut->ncolors; ++i) {
×
773
        indices[i] = i;
×
774
    }
775
    lut->kdnodes_count = 0;
×
776
    lut->kdtree_root = sixel_certlut_kdtree_build_recursive(lut,
×
777
                                                            indices,
778
                                                            lut->ncolors,
779
                                                            0);
780
    if (lut->kdtree_root < 0) {
×
781
        goto end;
×
782
    }
783
    status = SIXEL_OK;
×
784

UNCOV
785
end:
×
786
    free(indices);
×
787
    if (SIXEL_FAILED(status)) {
×
788
        free(lut->kdnodes);
×
789
        lut->kdnodes = NULL;
×
790
        lut->kdnodes_count = 0;
×
791
        lut->kdtree_root = -1;
×
792
    }
793

794
    return status;
×
795
}
796

797
static uint64_t
798
sixel_certlut_axis_distance(sixel_certlut_t const *lut, int diff, int axis)
×
799
{
800
    uint64_t weight;
801
    uint64_t abs_diff;
802

803
    abs_diff = (uint64_t)(diff < 0 ? -diff : diff);
×
804
    if (axis == 0) {
×
805
        weight = lut->wR2;
×
806
    } else if (axis == 1) {
×
807
        weight = lut->wG2;
×
808
    } else {
809
        weight = lut->wB2;
×
810
    }
811

812
    return weight * abs_diff * abs_diff;
×
813
}
814

815
static void
816
sixel_certlut_consider_candidate(sixel_certlut_t const *lut,
×
817
                                 int candidate,
818
                                 int32_t wr_r,
819
                                 int32_t wg_g,
820
                                 int32_t wb_b,
821
                                 int *best_idx,
822
                                 uint64_t *best_dist,
823
                                 int *second_idx,
824
                                 uint64_t *second_dist)
825
{
826
    uint64_t distance;
827

828
    distance = sixel_certlut_distance_precomputed(lut,
×
829
                                                  candidate,
830
                                                  wr_r,
831
                                                  wg_g,
832
                                                  wb_b);
833
    if (distance < *best_dist) {
×
834
        *second_dist = *best_dist;
×
835
        *second_idx = *best_idx;
×
836
        *best_dist = distance;
×
837
        *best_idx = candidate;
×
838
    } else if (distance < *second_dist) {
×
839
        *second_dist = distance;
×
840
        *second_idx = candidate;
×
841
    }
842
}
×
843

844
static void
845
sixel_certlut_kdtree_search(sixel_certlut_t const *lut,
×
846
                            int node_index,
847
                            int r,
848
                            int g,
849
                            int b,
850
                            int32_t wr_r,
851
                            int32_t wg_g,
852
                            int32_t wb_b,
853
                            int *best_idx,
854
                            uint64_t *best_dist,
855
                            int *second_idx,
856
                            uint64_t *second_dist)
857
{
858
    sixel_certlut_node_t const *node;
859
    int axis;
860
    int value;
861
    int diff;
862
    int near_child;
863
    int far_child;
864
    uint64_t axis_bound;
865
    int component;
866

867
    if (node_index < 0) {
×
868
        return;
×
869
    }
870
    node = &lut->kdnodes[node_index];
×
871
    sixel_certlut_consider_candidate(lut,
×
872
                                     node->index,
×
873
                                     wr_r,
874
                                     wg_g,
875
                                     wb_b,
876
                                     best_idx,
877
                                     best_dist,
878
                                     second_idx,
879
                                     second_dist);
880

881
    axis = (int)node->axis;
×
882
    value = sixel_certlut_palette_component(lut, node->index, axis);
×
883
    if (axis == 0) {
×
884
        component = r;
×
885
    } else if (axis == 1) {
×
886
        component = g;
×
887
    } else {
888
        component = b;
×
889
    }
890
    diff = component - value;
×
891
    if (diff <= 0) {
×
892
        near_child = node->left;
×
893
        far_child = node->right;
×
894
    } else {
895
        near_child = node->right;
×
896
        far_child = node->left;
×
897
    }
898
    if (near_child >= 0) {
×
899
        sixel_certlut_kdtree_search(lut,
×
900
                                    near_child,
901
                                    r,
902
                                    g,
903
                                    b,
904
                                    wr_r,
905
                                    wg_g,
906
                                    wb_b,
907
                                    best_idx,
908
                                    best_dist,
909
                                    second_idx,
910
                                    second_dist);
911
    }
912
    axis_bound = sixel_certlut_axis_distance(lut, diff, axis);
×
913
    if (far_child >= 0 && axis_bound <= *second_dist) {
×
914
        sixel_certlut_kdtree_search(lut,
×
915
                                    far_child,
916
                                    r,
917
                                    g,
918
                                    b,
919
                                    wr_r,
920
                                    wg_g,
921
                                    wb_b,
922
                                    best_idx,
923
                                    best_dist,
924
                                    second_idx,
925
                                    second_dist);
926
    }
927
}
928

929
static void
930
sixel_certlut_distance_pair(sixel_certlut_t const *lut, int r, int g, int b,
×
931
                            int *best_idx, int *second_idx,
932
                            uint64_t *best_dist, uint64_t *second_dist)
933
{
934
    int i;
935
    int best_candidate;
936
    int second_candidate;
937
    uint64_t best_value;
938
    uint64_t second_value;
939
    uint64_t distance;
940
    int rr;
941
    int gg;
942
    int bb;
943
    int32_t wr_r;
944
    int32_t wg_g;
945
    int32_t wb_b;
946

947
    best_candidate = (-1);
×
948
    second_candidate = (-1);
×
949
    best_value = UINT64_MAX;
×
950
    second_value = UINT64_MAX;
×
951
    rr = r;
×
952
    gg = g;
×
953
    bb = b;
×
954
    if (rr < 0) {
×
955
        rr = 0;
×
956
    } else if (rr > 255) {
×
957
        rr = 255;
×
958
    }
959
    if (gg < 0) {
×
960
        gg = 0;
×
961
    } else if (gg > 255) {
×
962
        gg = 255;
×
963
    }
964
    if (bb < 0) {
×
965
        bb = 0;
×
966
    } else if (bb > 255) {
×
967
        bb = 255;
×
968
    }
969
    wr_r = lut->wr_scale[rr];
×
970
    wg_g = lut->wg_scale[gg];
×
971
    wb_b = lut->wb_scale[bb];
×
972
    if (lut->kdnodes != NULL && lut->kdtree_root >= 0) {
×
973
        sixel_certlut_kdtree_search(lut,
×
974
                                    lut->kdtree_root,
×
975
                                    r,
976
                                    g,
977
                                    b,
978
                                    wr_r,
979
                                    wg_g,
980
                                    wb_b,
981
                                    &best_candidate,
982
                                    &best_value,
983
                                    &second_candidate,
984
                                    &second_value);
985
    } else {
986
        for (i = 0; i < lut->ncolors; ++i) {
×
987
            distance = sixel_certlut_distance_precomputed(lut,
×
988
                                                          i,
989
                                                          wr_r,
990
                                                          wg_g,
991
                                                          wb_b);
992
            if (distance < best_value) {
×
993
                second_value = best_value;
×
994
                second_candidate = best_candidate;
×
995
                best_value = distance;
×
996
                best_candidate = i;
×
997
            } else if (distance < second_value) {
×
998
                second_value = distance;
×
999
                second_candidate = i;
×
1000
            }
1001
        }
1002
    }
1003
    if (second_candidate < 0) {
×
1004
        second_candidate = best_candidate;
×
1005
        second_value = best_value;
×
1006
    }
1007
    *best_idx = best_candidate;
×
1008
    *second_idx = second_candidate;
×
1009
    *best_dist = best_value;
×
1010
    *second_dist = second_value;
×
1011
}
×
1012

1013
static uint8_t
1014
sixel_certlut_fallback(sixel_certlut_t const *lut, int r, int g, int b)
×
1015
{
1016
    int best_idx;
1017
    int second_idx;
1018
    uint64_t best_dist;
1019
    uint64_t second_dist;
1020

1021
    best_idx = -1;
×
1022
    second_idx = -1;
×
1023
    best_dist = 0U;
×
1024
    second_dist = 0U;
×
1025
    if (lut == NULL) {
×
1026
        return 0U;
×
1027
    }
1028
    /*
1029
     * The lazy builder may fail when allocations run out.  Fall back to a
1030
     * direct brute-force palette search so lookups still succeed even in low
1031
     * memory conditions.
1032
     */
1033
    sixel_certlut_distance_pair(lut,
×
1034
                                r,
1035
                                g,
1036
                                b,
1037
                                &best_idx,
1038
                                &second_idx,
1039
                                &best_dist,
1040
                                &second_dist);
1041
    if (best_idx < 0) {
×
1042
        return 0U;
×
1043
    }
1044

1045
    return (uint8_t)best_idx;
×
1046
}
1047

1048
SIXELSTATUS
1049
sixel_certlut_build_cell(sixel_certlut_t *lut, uint32_t *cell,
×
1050
                         int rmin, int gmin, int bmin, int size)
1051
{
1052
    SIXELSTATUS status;
1053
    int cr;
1054
    int cg;
1055
    int cb;
1056
    int best_idx;
1057
    int second_idx;
1058
    uint64_t best_dist;
1059
    uint64_t second_dist;
1060
    uint32_t offset;
1061
    int branch_status;
1062
    uint8_t *pool_before;
1063
    size_t pool_size_before;
1064
    uint32_t cell_offset;
1065
    int cell_in_pool;
1066

1067
    if (cell == NULL || lut == NULL) {
×
1068
        status = SIXEL_BAD_ARGUMENT;
×
1069
        goto end;
×
1070
    }
1071
    if (*cell == 0U) {
×
1072
#ifdef DEBUG_LUT_TRACE
1073
        fprintf(stderr,
1074
                "build_cell rmin=%d gmin=%d bmin=%d size=%d\n",
1075
                rmin,
1076
                gmin,
1077
                bmin,
1078
                size);
1079
#endif
1080
    }
1081
    if (*cell != 0U) {
×
1082
        status = SIXEL_OK;
×
1083
        goto end;
×
1084
    }
1085

1086
    /*
1087
     * Each node represents an axis-aligned cube in RGB space.  The builder
1088
     * certifies the dominant palette index by checking the distance gap at
1089
     * the cell center.  When certification fails the cube is split into eight
1090
     * octants backed by a pool block.  Children remain unbuilt until lookups
1091
     * descend into them, keeping the workload proportional to actual queries.
1092
     */
1093
    status = SIXEL_FALSE;
×
1094
    sixel_certlut_cell_center(rmin, gmin, bmin, size, &cr, &cg, &cb);
×
1095
    sixel_certlut_distance_pair(lut, cr, cg, cb, &best_idx, &second_idx,
×
1096
                              &best_dist, &second_dist);
1097
    if (best_idx < 0) {
×
1098
        best_idx = 0;
×
1099
    }
1100
    if (size == 1) {
×
1101
        sixel_certlut_assign_leaf(cell, best_idx);
×
1102
#ifdef DEBUG_LUT_TRACE
1103
        fprintf(stderr,
1104
                "  leaf idx=%d\n",
1105
                best_idx);
1106
#endif
1107
        status = SIXEL_OK;
×
1108
        goto end;
×
1109
    }
1110
    if (sixel_certlut_is_cell_safe(lut, best_idx, second_idx, size,
×
1111
                                 best_dist, second_dist)) {
1112
        sixel_certlut_assign_leaf(cell, best_idx);
×
1113
#ifdef DEBUG_LUT_TRACE
1114
        fprintf(stderr,
1115
                "  safe leaf idx=%d\n",
1116
                best_idx);
1117
#endif
1118
        status = SIXEL_OK;
×
1119
        goto end;
×
1120
    }
1121
    pool_before = lut->pool;
×
1122
    pool_size_before = lut->pool_size;
×
1123
    cell_in_pool = 0;
×
1124
    cell_offset = 0U;
×
1125
    /*
1126
     * The pool may grow while building descendants.  Remember the caller's
1127
     * offset so the cell pointer can be refreshed after realloc moves the
1128
     * backing storage.
1129
     */
1130
    if (pool_before != NULL) {
×
1131
        if ((uint8_t *)(void *)cell >= pool_before
×
1132
                && (size_t)((uint8_t *)(void *)cell - pool_before)
×
1133
                        < pool_size_before) {
1134
            cell_in_pool = 1;
×
1135
            cell_offset = (uint32_t)((uint8_t *)(void *)cell - pool_before);
×
1136
        }
1137
    }
1138
    offset = sixel_certlut_pool_alloc(lut, &branch_status);
×
1139
    if (branch_status != SIXEL_OK) {
×
1140
        goto end;
×
1141
    }
1142
    if (cell_in_pool != 0) {
×
1143
        cell = (uint32_t *)(void *)(lut->pool + cell_offset);
×
1144
    }
1145
    sixel_certlut_assign_branch(cell, offset);
×
1146
#ifdef DEBUG_LUT_TRACE
1147
    fprintf(stderr,
1148
            "  branch offset=%u\n",
1149
            offset);
1150
#endif
1151
    status = SIXEL_OK;
×
1152

UNCOV
1153
end:
×
1154
    return status;
×
1155
}
1156

1157
SIXELSTATUS
1158
sixel_certlut_build(sixel_certlut_t *lut, sixel_certlut_color_t const *palette,
×
1159
                    int ncolors, int wR, int wG, int wB)
1160
{
1161
    SIXELSTATUS status;
1162
    int initialized;
1163
    size_t level0_count;
1164
    status = SIXEL_FALSE;
×
1165
    initialized = sixel_certlut_init(lut);
×
1166
    if (SIXEL_FAILED(initialized)) {
×
1167
        goto end;
×
1168
    }
1169
    lut->palette = palette;
×
1170
    lut->ncolors = ncolors;
×
1171
    sixel_certlut_weight_init(lut, wR, wG, wB);
×
1172
    status = sixel_certlut_prepare_palette_terms(lut);
×
1173
    if (SIXEL_FAILED(status)) {
×
1174
        goto end;
×
1175
    }
1176
    status = sixel_certlut_kdtree_build(lut);
×
1177
    if (SIXEL_FAILED(status)) {
×
1178
        goto end;
×
1179
    }
1180
    level0_count = (size_t)64 * (size_t)64 * (size_t)64;
×
1181
    lut->level0 = (uint32_t *)calloc(level0_count, sizeof(uint32_t));
×
1182
    if (lut->level0 == NULL) {
×
1183
        goto end;
×
1184
    }
1185
    /*
1186
     * Level 0 cells start uninitialized.  The lookup routine materializes
1187
     * individual subtrees on demand so we avoid evaluating the entire
1188
     * 64x64x64 grid upfront.
1189
     */
1190
    status = SIXEL_OK;
×
1191

UNCOV
1192
end:
×
1193
    if (SIXEL_FAILED(status)) {
×
1194
        sixel_certlut_release(lut);
×
1195
    }
1196
    return status;
×
1197
}
1198

1199
uint8_t
1200
sixel_certlut_lookup(sixel_certlut_t *lut, uint8_t r, uint8_t g, uint8_t b)
×
1201
{
1202
    uint32_t entry;
1203
    uint32_t offset;
1204
    uint32_t index;
1205
    uint32_t *children;
1206
    uint32_t *cell;
1207
    int shift;
1208
    int child;
1209
    int status;
1210
    int size;
1211
    int rmin;
1212
    int gmin;
1213
    int bmin;
1214
    int step;
1215
    if (lut == NULL || lut->level0 == NULL) {
×
1216
        return 0U;
×
1217
    }
1218
    /*
1219
     * Cells are created lazily.  A zero entry indicates an uninitialized
1220
     * subtree, so the builder is invoked with the cube bounds of the current
1221
     * traversal.  Should allocation fail we fall back to a direct brute-force
1222
     * palette search for the queried pixel.
1223
     */
1224
    index = ((uint32_t)(r >> 2) << 12)
×
1225
          | ((uint32_t)(g >> 2) << 6)
×
1226
          | (uint32_t)(b >> 2);
×
1227
    cell = lut->level0 + index;
×
1228
    size = 4;
×
1229
    rmin = (int)(r & 0xfc);
×
1230
    gmin = (int)(g & 0xfc);
×
1231
    bmin = (int)(b & 0xfc);
×
1232
    entry = *cell;
×
1233
    if (entry == 0U) {
×
1234
#ifdef DEBUG_LUT_TRACE
1235
        fprintf(stderr,
1236
                "lookup build level0 r=%u g=%u b=%u\n",
1237
                (unsigned int)r,
1238
                (unsigned int)g,
1239
                (unsigned int)b);
1240
#endif
1241
        status = sixel_certlut_build_cell(lut, cell, rmin, gmin, bmin, size);
×
1242
        if (SIXEL_FAILED(status)) {
×
1243
            return sixel_certlut_fallback(lut,
×
1244
                                          (int)r,
1245
                                          (int)g,
1246
                                          (int)b);
1247
        }
1248
        entry = *cell;
×
1249
    }
1250
    shift = 1;
×
1251
    while ((entry & 0x80000000U) == 0U) {
×
1252
        offset = entry & 0x3fffffffU;
×
1253
        children = (uint32_t *)(void *)(lut->pool + offset);
×
1254
        child = (((int)(r >> shift) & 1) << 2)
×
1255
              | (((int)(g >> shift) & 1) << 1)
×
1256
              | ((int)(b >> shift) & 1);
×
1257
#ifdef DEBUG_LUT_TRACE
1258
        fprintf(stderr,
1259
                "descend child=%d size=%d offset=%u\n",
1260
                child,
1261
                size,
1262
                offset);
1263
#endif
1264
        step = size / 2;
×
1265
        if (step <= 0) {
×
1266
            step = 1;
×
1267
        }
1268
        rmin += step * ((child >> 2) & 1);
×
1269
        gmin += step * ((child >> 1) & 1);
×
1270
        bmin += step * (child & 1);
×
1271
        size = step;
×
1272
        cell = children + (size_t)child;
×
1273
        entry = *cell;
×
1274
        if (entry == 0U) {
×
1275
#ifdef DEBUG_LUT_TRACE
1276
            fprintf(stderr,
1277
                    "lookup build child size=%d rmin=%d gmin=%d bmin=%d\n",
1278
                    size,
1279
                    rmin,
1280
                    gmin,
1281
                    bmin);
1282
#endif
1283
            status = sixel_certlut_build_cell(lut,
×
1284
                                              cell,
1285
                                              rmin,
1286
                                              gmin,
1287
                                              bmin,
1288
                                              size);
1289
            if (SIXEL_FAILED(status)) {
×
1290
                return sixel_certlut_fallback(lut,
×
1291
                                              (int)r,
1292
                                              (int)g,
1293
                                              (int)b);
1294
            }
1295
            children = (uint32_t *)(void *)(lut->pool + offset);
×
1296
            cell = children + (size_t)child;
×
1297
            entry = *cell;
×
1298
        }
1299
        if (size == 1) {
×
1300
            break;
×
1301
        }
1302
        if (shift == 0) {
×
1303
            break;
×
1304
        }
1305
        --shift;
×
1306
    }
1307

1308
    return (uint8_t)(entry & 0xffU);
×
1309
}
1310

1311
void
1312
sixel_certlut_free(sixel_certlut_t *lut)
×
1313
{
1314
    sixel_certlut_release(lut);
×
1315
    if (lut != NULL) {
×
1316
        lut->palette = NULL;
×
1317
        lut->ncolors = 0;
×
1318
    }
1319
}
×
1320

1321
SIXELSTATUS
1322
sixel_lut_new(sixel_lut_t **out,
156✔
1323
              int policy,
1324
              sixel_allocator_t *allocator)
1325
{
1326
    sixel_lut_t *lut;
1327
    SIXELSTATUS status;
1328

1329
    if (out == NULL) {
156!
1330
        return SIXEL_BAD_ARGUMENT;
×
1331
    }
1332
    if (allocator == NULL) {
156!
1333
        return SIXEL_BAD_ARGUMENT;
×
1334
    }
1335

1336
    lut = (sixel_lut_t *)malloc(sizeof(sixel_lut_t));
156✔
1337
    if (lut == NULL) {
156!
1338
        return SIXEL_BAD_ALLOCATION;
×
1339
    }
1340
    memset(lut, 0, sizeof(sixel_lut_t));
156✔
1341
    lut->allocator = allocator;
156✔
1342
    lut->policy = sixel_lut_policy_normalize(policy);
156✔
1343
    lut->depth = 0;
156✔
1344
    lut->ncolors = 0;
156✔
1345
    lut->complexion = 1;
156✔
1346
    lut->palette = NULL;
156✔
1347
    lut->quant.channel_shift = 0U;
156✔
1348
    lut->quant.channel_bits = 8U;
156✔
1349
    lut->quant.channel_mask = 0xffU;
156✔
1350
    lut->dense = NULL;
156✔
1351
    lut->dense_size = 0U;
156✔
1352
    lut->dense_ready = 0;
156✔
1353
    lut->cert_ready = 0;
156✔
1354
    status = sixel_certlut_init(&lut->cert);
156✔
1355
    if (SIXEL_FAILED(status)) {
156!
1356
        free(lut);
×
1357
        sixel_helper_set_additional_message(
×
1358
            "sixel_lut_new: unable to initialize certlut state.");
1359
        return status;
×
1360
    }
1361

1362
    *out = lut;
156✔
1363

1364
    return SIXEL_OK;
156✔
1365
}
1366

1367
SIXELSTATUS
1368
sixel_lut_configure(sixel_lut_t *lut,
316✔
1369
                    unsigned char const *palette,
1370
                    int depth,
1371
                    int ncolors,
1372
                    int complexion,
1373
                    int wR,
1374
                    int wG,
1375
                    int wB,
1376
                    int policy)
1377
{
1378
    SIXELSTATUS status;
1379
    int normalized;
1380

1381
    if (lut == NULL) {
316!
1382
        return SIXEL_BAD_ARGUMENT;
×
1383
    }
1384
    if (palette == NULL) {
316!
1385
        return SIXEL_BAD_ARGUMENT;
×
1386
    }
1387
    if (depth <= 0) {
316!
1388
        return SIXEL_BAD_ARGUMENT;
×
1389
    }
1390

1391
    lut->palette = palette;
316✔
1392
    lut->depth = depth;
316✔
1393
    lut->ncolors = ncolors;
316✔
1394
    lut->complexion = complexion;
316✔
1395
    normalized = sixel_lut_policy_normalize(policy);
316✔
1396
    lut->policy = normalized;
316✔
1397
    lut->quant = sixel_lut_quant_make((unsigned int)depth, normalized);
316✔
1398
    lut->dense_ready = 0;
316✔
1399
    lut->cert_ready = 0;
316✔
1400

1401
    if (sixel_lut_policy_uses_cache(normalized)) {
316!
1402
        if (depth != 3) {
316!
1403
            sixel_helper_set_additional_message(
×
1404
                "sixel_lut_configure: fast LUT requires RGB pixels.");
1405
            return SIXEL_BAD_ARGUMENT;
×
1406
        }
1407
        status = sixel_lut_prepare_cache(lut);
316✔
1408
        if (SIXEL_FAILED(status)) {
316!
1409
            return status;
×
1410
        }
1411
    } else {
1412
        sixel_lut_release_cache(lut);
×
1413
        status = SIXEL_OK;
×
1414
    }
1415

1416
    if (normalized == SIXEL_LUT_POLICY_CERTLUT) {
316!
1417
        status = sixel_certlut_build(&lut->cert,
×
1418
                                     (sixel_certlut_color_t const *)palette,
1419
                                     ncolors,
1420
                                     wR,
1421
                                     wG,
1422
                                     wB);
1423
        if (SIXEL_FAILED(status)) {
×
1424
            return status;
×
1425
        }
1426
        lut->cert_ready = 1;
×
1427
    }
1428

1429
    return SIXEL_OK;
316✔
1430
}
1431

1432
int
1433
sixel_lut_map_pixel(sixel_lut_t *lut, unsigned char const *pixel)
21,098,740✔
1434
{
1435
    if (lut == NULL || pixel == NULL) {
21,098,740!
1436
        return 0;
×
1437
    }
1438
    if (lut->policy == SIXEL_LUT_POLICY_CERTLUT) {
21,098,740!
1439
        if (!lut->cert_ready) {
×
1440
            return 0;
×
1441
        }
1442
        return (int)sixel_certlut_lookup(&lut->cert,
×
1443
                                         pixel[0],
×
1444
                                         pixel[1],
×
1445
                                         pixel[2]);
×
1446
    }
1447

1448
    return sixel_lut_lookup_fast(lut, pixel);
21,098,740✔
1449
}
1450

1451
void
1452
sixel_lut_clear(sixel_lut_t *lut)
156✔
1453
{
1454
    if (lut == NULL) {
156!
1455
        return;
×
1456
    }
1457

1458
    sixel_lut_release_cache(lut);
156✔
1459
    if (lut->cert_ready) {
156!
1460
        sixel_certlut_free(&lut->cert);
×
1461
        lut->cert_ready = 0;
×
1462
    }
1463
    lut->palette = NULL;
156✔
1464
    lut->depth = 0;
156✔
1465
    lut->ncolors = 0;
156✔
1466
    lut->complexion = 1;
156✔
1467
}
1468

1469
void
1470
sixel_lut_unref(sixel_lut_t *lut)
156✔
1471
{
1472
    if (lut == NULL) {
156!
1473
        return;
×
1474
    }
1475

1476
    sixel_lut_clear(lut);
156✔
1477
    free(lut);
156✔
1478
}
1479

1480
/* emacs Local Variables:      */
1481
/* emacs mode: c               */
1482
/* emacs tab-width: 4          */
1483
/* emacs indent-tabs-mode: nil */
1484
/* emacs c-basic-offset: 4     */
1485
/* emacs End:                  */
1486
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1487
/* 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