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

saitoha / libsixel / 19776286632

29 Nov 2025 12:20AM UTC coverage: 41.017% (-0.3%) from 41.338%
19776286632

push

github

saitoha
build: remove unused status in reversible snap test

9964 of 36344 branches covered (27.42%)

13002 of 31699 relevant lines covered (41.02%)

1178071.22 hits per line

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

32.2
/src/lookup-8bit.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) 2025 libsixel developers. See `AUTHORS`.
27
 * Copyright (c) 2014-2018 Hayaki Saito
28
 *
29
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
30
 * this software and associated documentation files (the "Software"), to deal in
31
 * the Software without restriction, including without limitation the rights to
32
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
33
 * the Software, and to permit persons to whom the Software is furnished to do so,
34
 * subject to the following conditions:
35
 *
36
 * The above copyright notice and this permission notice shall be included in all
37
 * copies or substantial portions of the Software.
38
 *
39
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
41
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
42
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
43
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
44
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
45
 */
46

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

52
#include "config.h"
53

54
#include <limits.h>
55
#include <stdint.h>
56
#include <stdio.h>
57
#include <stdlib.h>
58
#include <string.h>
59
#if HAVE_MATH_H
60
# include <math.h>
61
#endif  /* HAVE_MATH_H */
62
#if defined(_MSC_VER) && defined(HAVE_INTRIN_H)
63
#include <intrin.h>
64
#endif
65

66
#include <sixel.h>
67

68
#include "status.h"
69
#include "compat_stub.h"
70
#include "allocator.h"
71
#include "lookup-8bit.h"
72

73
#define SIXEL_LUT_BRANCH_FLAG 0x40000000U
74
/* #define DEBUG_LUT_TRACE 1 */
75

76
enum { SIXEL_CERTLUT_COMPONENTS = 3 };
77

78
typedef struct sixel_certlut_color {
79
    uint8_t comp[SIXEL_CERTLUT_COMPONENTS];
80
} sixel_certlut_color_t;
81

82
typedef struct sixel_certlut_node {
83
    int index;
84
    int left;
85
    int right;
86
    unsigned char axis;
87
} sixel_certlut_node_t;
88

89
struct sixel_certlut {
90
    uint32_t *level0;
91
    uint8_t *pool;
92
    uint32_t pool_size;
93
    uint32_t pool_capacity;
94
    int weights[SIXEL_CERTLUT_COMPONENTS];
95
    uint64_t weights_sq[SIXEL_CERTLUT_COMPONENTS];
96
    int32_t scales[SIXEL_CERTLUT_COMPONENTS][256];
97
    int32_t *weight_palette[SIXEL_CERTLUT_COMPONENTS];
98
    sixel_certlut_color_t const *palette;
99
    int ncolors;
100
    sixel_certlut_node_t *kdnodes;
101
    int kdnodes_count;
102
    int kdtree_root;
103
};
104

105
/* Sentinel value used to detect empty dense LUT slots. */
106
#define SIXEL_LUT_DENSE_EMPTY (-1)
107

108
/*
109
 * Compute quantization parameters for the dense cache.  The selection mirrors
110
 * the historic histogram helper so existing 5bit/6bit behaviour stays intact
111
 * while still allowing "none" and "certlut" to bypass the cache entirely.
112
 */
113
static sixel_lookup_8bit_quantization_t
114
sixel_lookup_8bit_quant_make(unsigned int depth, int policy)
640✔
115
{
116
    sixel_lookup_8bit_quantization_t quant;
117
    unsigned int shift;
118

119
    shift = 2U;
640✔
120
    if (depth > 3U) {
640!
121
        shift = 3U;
×
122
    }
123
    if (policy == SIXEL_LUT_POLICY_5BIT) {
640!
124
        shift = 3U;
×
125
    } else if (policy == SIXEL_LUT_POLICY_6BIT) {
640!
126
        shift = 2U;
640✔
127
        if (depth > 3U) {
640!
128
            shift = 3U;
×
129
        }
130
    } else if (policy == SIXEL_LUT_POLICY_NONE
×
131
               || policy == SIXEL_LUT_POLICY_CERTLUT) {
×
132
        shift = 0U;
×
133
    }
134

135
    quant.channel_shift = shift;
640✔
136
    quant.channel_bits = 8U - shift;
640✔
137
    quant.channel_mask = (1U << quant.channel_bits) - 1U;
640✔
138

139
    return quant;
640✔
140
}
141

142
/*
143
 * Return the dense table size for the active quantization.  The loop saturates
144
 * at SIZE_MAX so the later allocation guard can emit a friendly error message
145
 * instead of overflowing.
146
 */
147
static size_t
148
sixel_lookup_8bit_dense_size(unsigned int depth,
640✔
149
                     sixel_lookup_8bit_quantization_t const *quant)
150
{
151
    size_t size;
152
    unsigned int exponent;
153
    unsigned int i;
154

155
    size = 1U;
640✔
156
    exponent = depth * quant->channel_bits;
640✔
157
    for (i = 0U; i < exponent; ++i) {
12,160✔
158
        if (size > SIZE_MAX / 2U) {
11,520!
159
            size = SIZE_MAX;
×
160
            break;
×
161
        }
162
        size <<= 1U;
11,520✔
163
    }
164

165
    return size;
640✔
166
}
167

168
/*
169
 * Pack a pixel into the dense cache index.  The rounding matches the old
170
 * histogram_pack_color() implementation to keep cached answers stable.
171
 */
172
static unsigned int
173
sixel_lookup_8bit_pack_color(unsigned char const *pixel,
43,277,480✔
174
                     unsigned int depth,
175
                     sixel_lookup_8bit_quantization_t const *quant)
176
{
177
    unsigned int packed;
178
    unsigned int bits;
179
    unsigned int shift;
180
    unsigned int plane;
181
    unsigned int component;
182
    unsigned int rounded;
183
    unsigned int mask;
184

185
    packed = 0U;
43,277,480✔
186
    bits = quant->channel_bits;
43,277,480✔
187
    shift = quant->channel_shift;
43,277,480✔
188
    mask = quant->channel_mask;
43,277,480✔
189

190
    for (plane = 0U; plane < depth; ++plane) {
173,109,920✔
191
        component = (unsigned int)pixel[depth - 1U - plane];
129,832,440✔
192
        if (shift > 0U) {
129,832,440!
193
            rounded = (component + (1U << (shift - 1U))) >> shift;
129,832,440✔
194
            if (rounded > mask) {
129,832,440✔
195
                rounded = mask;
12,020,576✔
196
            }
197
        } else {
198
            rounded = component & mask;
×
199
        }
200
        packed |= rounded << (plane * bits);
129,832,440✔
201
    }
202

203
    return packed;
43,277,480✔
204
}
205

206
static int
207
sixel_lookup_8bit_policy_normalize(int policy)
956✔
208
{
209
    int normalized;
210

211
    normalized = policy;
956✔
212
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
956!
213
        normalized = SIXEL_LUT_POLICY_6BIT;
×
214
    } else if (normalized != SIXEL_LUT_POLICY_5BIT
956!
215
               && normalized != SIXEL_LUT_POLICY_6BIT
956!
216
               && normalized != SIXEL_LUT_POLICY_CERTLUT
×
217
               && normalized != SIXEL_LUT_POLICY_NONE) {
×
218
        normalized = SIXEL_LUT_POLICY_6BIT;
×
219
    }
220

221
    return normalized;
956✔
222
}
223

224
static int
225
sixel_lookup_8bit_policy_uses_cache(int policy)
1,280✔
226
{
227
    if (policy == SIXEL_LUT_POLICY_CERTLUT
1,280!
228
        || policy == SIXEL_LUT_POLICY_NONE) {
1,280!
229
        return 0;
×
230
    }
231

232
    return 1;
1,280✔
233
}
234

235
static void
236
sixel_lookup_8bit_release_cache(sixel_lookup_8bit_t *lut)
316✔
237
{
238
    if (lut == NULL || lut->dense == NULL) {
316!
239
        return;
×
240
    }
241

242
    sixel_allocator_free(lut->allocator, lut->dense);
316✔
243
    lut->dense = NULL;
316✔
244
    lut->dense_size = 0U;
316✔
245
    lut->dense_ready = 0;
316✔
246
}
247

248
static SIXELSTATUS
249
sixel_lookup_8bit_prepare_cache(sixel_lookup_8bit_t *lut)
640✔
250
{
251
    size_t expected;
252
    size_t bytes;
253
    size_t index;
254

255
    if (lut == NULL) {
640!
256
        return SIXEL_BAD_ARGUMENT;
×
257
    }
258
    if (!sixel_lookup_8bit_policy_uses_cache(lut->policy)) {
640!
259
        return SIXEL_OK;
×
260
    }
261
    if (lut->allocator == NULL) {
640!
262
        return SIXEL_BAD_ARGUMENT;
×
263
    }
264

265
    /* Allocate a dense RGB quantization table for 5/6bit policies.
266
     * The packed index matches sixel_lookup_8bit_pack_color() so each slot
267
     * can store the resolved palette entry or remain empty.
268
     */
269
    expected = sixel_lookup_8bit_dense_size((unsigned int)lut->depth,
640✔
270
                                    &lut->quant);
640✔
271
    if (expected == 0U) {
640!
272
        return SIXEL_BAD_ARGUMENT;
×
273
    }
274
    if (expected > SIZE_MAX / sizeof(int32_t)) {
640!
275
        sixel_helper_set_additional_message(
×
276
            "sixel_lookup_8bit_prepare_cache: dense table too large.");
277
        return SIXEL_BAD_ALLOCATION;
×
278
    }
279

280
    if (lut->dense != NULL && lut->dense_size != expected) {
640!
281
        sixel_lookup_8bit_release_cache(lut);
×
282
    }
283

284
    if (lut->dense == NULL) {
640✔
285
        bytes = expected * sizeof(int32_t);
316✔
286
        lut->dense = (int32_t *)sixel_allocator_malloc(lut->allocator,
316✔
287
                                                       bytes);
288
        if (lut->dense == NULL) {
316!
289
            sixel_helper_set_additional_message(
×
290
                "sixel_lookup_8bit_prepare_cache: allocation failed.");
291
            return SIXEL_BAD_ALLOCATION;
×
292
        }
293
    }
294

295
    for (index = 0U; index < expected; ++index) {
167,772,800✔
296
        lut->dense[index] = SIXEL_LUT_DENSE_EMPTY;
167,772,160✔
297
    }
298

299
    lut->dense_size = expected;
640✔
300
    lut->dense_ready = 1;
640✔
301

302
    return SIXEL_OK;
640✔
303
}
304

305
static int
306
sixel_lookup_8bit_lookup_fast(sixel_lookup_8bit_t *lut, unsigned char const *pixel)
43,277,480✔
307
{
308
    int result;
309
    int diff;
310
    int i;
311
    int distant;
312
    unsigned char const *entry;
313
    unsigned char const *end;
314
    int pixel0;
315
    int pixel1;
316
    int pixel2;
317
    int delta;
318
    unsigned int bucket;
319
    int32_t cached;
320

321
    if (lut == NULL || pixel == NULL) {
43,277,480!
322
        return 0;
×
323
    }
324
    if (lut->palette == NULL || lut->ncolors <= 0) {
43,277,480!
325
        return 0;
×
326
    }
327

328
    bucket = 0U;
43,277,480✔
329
    cached = SIXEL_LUT_DENSE_EMPTY;
43,277,480✔
330
    if (lut->dense_ready && lut->dense != NULL) {
43,277,480!
331
        bucket = sixel_lookup_8bit_pack_color(pixel,
43,277,480✔
332
                                      (unsigned int)lut->depth,
43,277,480✔
333
                                      &lut->quant);
43,277,480✔
334
        if ((size_t)bucket < lut->dense_size) {
43,277,480!
335
            cached = lut->dense[bucket];
43,277,480✔
336
            if (cached >= 0) {
43,277,480✔
337
                return cached;
41,311,560✔
338
            }
339
        }
340
    }
341

342
    result = (-1);
1,965,920✔
343
    diff = INT_MAX;
1,965,920✔
344
    /* Linear palette scan remains as a safety net when the dense
345
     * lookup does not have an answer yet.
346
     */
347
    entry = lut->palette;
1,965,920✔
348
    end = lut->palette + (size_t)lut->ncolors * (size_t)lut->depth;
1,965,920✔
349
    pixel0 = (int)pixel[0];
1,965,920✔
350
    pixel1 = (int)pixel[1];
1,965,920✔
351
    pixel2 = (int)pixel[2];
1,965,920✔
352
    for (i = 0; entry < end; ++i, entry += lut->depth) {
333,763,608✔
353
        delta = pixel0 - (int)entry[0];
331,797,688✔
354
        distant = delta * delta * lut->complexion;
331,797,688✔
355
        delta = pixel1 - (int)entry[1];
331,797,688✔
356
        distant += delta * delta;
331,797,688✔
357
        delta = pixel2 - (int)entry[2];
331,797,688✔
358
        distant += delta * delta;
331,797,688✔
359
        if (distant < diff) {
331,797,688✔
360
            diff = distant;
14,034,444✔
361
            result = i;
14,034,444✔
362
        }
363
    }
364

365
    if (lut->dense_ready && lut->dense != NULL && result >= 0) {
1,965,920!
366
        if ((size_t)bucket < lut->dense_size) {
1,965,920!
367
            lut->dense[bucket] = result;
1,965,920✔
368
        }
369
    }
370

371
    if (result < 0) {
1,965,920!
372
        result = 0;
×
373
    }
374

375
    return result;
1,965,920✔
376
}
377

378
static int
379
sixel_certlut_init(sixel_certlut_t *lut)
316✔
380
{
381
    int status;
382
    int component;
383

384
    status = SIXEL_FALSE;
316✔
385
    if (lut == NULL) {
316!
386
        goto end;
×
387
    }
388

389
    lut->level0 = NULL;
316✔
390
    lut->pool = NULL;
316✔
391
    lut->pool_size = 0U;
316✔
392
    lut->pool_capacity = 0U;
316✔
393
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
1,264✔
394
        lut->weights[component] = 1;
948✔
395
        lut->weights_sq[component] = 1U;
948✔
396
        memset(lut->scales[component], 0,
948✔
397
               sizeof(lut->scales[component]));
398
        lut->weight_palette[component] = NULL;
948✔
399
    }
400
    lut->palette = NULL;
316✔
401
    lut->ncolors = 0;
316✔
402
    lut->kdnodes = NULL;
316✔
403
    lut->kdnodes_count = 0;
316✔
404
    lut->kdtree_root = -1;
316✔
405
    status = SIXEL_OK;
316✔
406

407
end:
316✔
408
    return status;
316✔
409
}
410

411
static void
412
sixel_certlut_release(sixel_certlut_t *lut)
316✔
413
{
414
    int component;
415

416
    if (lut == NULL) {
316!
417
        return;
×
418
    }
419
    free(lut->level0);
316✔
420
    free(lut->pool);
316✔
421
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
1,264✔
422
        free(lut->weight_palette[component]);
948✔
423
        lut->weight_palette[component] = NULL;
948✔
424
    }
425
    free(lut->kdnodes);
316✔
426
    lut->level0 = NULL;
316✔
427
    lut->pool = NULL;
316✔
428
    lut->pool_size = 0U;
316✔
429
    lut->pool_capacity = 0U;
316✔
430
    lut->kdnodes = NULL;
316✔
431
    lut->kdnodes_count = 0;
316✔
432
    lut->kdtree_root = -1;
316✔
433
}
434

435
static int
436
sixel_certlut_prepare_palette_terms(sixel_certlut_t *lut)
×
437
{
438
    int status;
439
    size_t count;
440
    int index;
441
    int component;
442
    int32_t *terms[SIXEL_CERTLUT_COMPONENTS];
443

444
    status = SIXEL_FALSE;
×
445
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
446
        terms[component] = NULL;
×
447
    }
448
    if (lut == NULL) {
×
449
        goto end;
×
450
    }
451
    if (lut->ncolors <= 0) {
×
452
        status = SIXEL_OK;
×
453
        goto end;
×
454
    }
455
    count = (size_t)lut->ncolors;
×
456
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
457
        terms[component] = (int32_t *)malloc(count * sizeof(int32_t));
×
458
        if (terms[component] == NULL) {
×
459
            goto end;
×
460
        }
461
    }
462
    for (index = 0; index < lut->ncolors; ++index) {
×
463
        for (component = 0; component < SIXEL_CERTLUT_COMPONENTS;
×
464
                ++component) {
×
465
            terms[component][index]
×
466
                = lut->weights[component]
×
467
                * (int)lut->palette[index].comp[component];
×
468
        }
469
    }
470
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
471
        free(lut->weight_palette[component]);
×
472
        lut->weight_palette[component] = terms[component];
×
473
        terms[component] = NULL;
×
474
    }
475
    status = SIXEL_OK;
×
476

477
end:
×
478
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
479
        free(terms[component]);
×
480
    }
481
    return status;
×
482
}
483

484
static void
485
sixel_certlut_cell_center(int comp1_min,
×
486
                          int comp2_min,
487
                          int comp3_min,
488
                          int size,
489
                          int *comp1_center,
490
                          int *comp2_center,
491
                          int *comp3_center)
492
{
493
    int half;
494

495
    half = size / 2;
×
496
    *comp1_center = comp1_min + half;
×
497
    *comp2_center = comp2_min + half;
×
498
    *comp3_center = comp3_min + half;
×
499
    if (size == 1) {
×
500
        *comp1_center = comp1_min;
×
501
        *comp2_center = comp2_min;
×
502
        *comp3_center = comp3_min;
×
503
    }
504
}
×
505

506
static void
507
sixel_certlut_weight_init(sixel_certlut_t *lut,
×
508
                          int comp1_weight,
509
                          int comp2_weight,
510
                          int comp3_weight)
511
{
512
    int component;
513
    int i;
514
    int input[SIXEL_CERTLUT_COMPONENTS];
515

516
    input[0] = comp1_weight;
×
517
    input[1] = comp2_weight;
×
518
    input[2] = comp3_weight;
×
519
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
520
        lut->weights[component] = input[component];
×
521
        lut->weights_sq[component]
522
            = (uint64_t)input[component] * (uint64_t)input[component];
×
523
        for (i = 0; i < 256; ++i) {
×
524
            lut->scales[component][i] = input[component] * i;
×
525
        }
526
    }
527
}
×
528

529
static uint64_t
530
sixel_certlut_distance_precomputed(sixel_certlut_t const *lut,
×
531
                                   int index,
532
                                   int32_t const scaled_components[])
533
{
534
    uint64_t distance;
535
    int64_t diff;
536
    int component;
537

538
    distance = 0U;
×
539
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
540
        diff = (int64_t)scaled_components[component]
×
541
             - (int64_t)lut->weight_palette[component][index];
×
542
        distance += (uint64_t)(diff * diff);
×
543
    }
544

545
    return distance;
×
546
}
547

548
static int
549
sixel_certlut_is_cell_safe(sixel_certlut_t const *lut, int best_idx,
×
550
                         int second_idx, int size, uint64_t best_dist,
551
                         uint64_t second_dist)
552
{
553
    uint64_t delta_sq;
554
    uint64_t rhs;
555
    uint64_t weight_term;
556
    int64_t delta;
557
    int component;
558

559
    if (best_idx < 0 || second_idx < 0) {
×
560
        return 1;
×
561
    }
562

563
    /*
564
     * The certification bound compares the squared distance gap against the
565
     * palette separation scaled by the cube diameter.  If the gap wins the
566
     * entire cube maps to the current best palette entry.
567
     */
568
    delta_sq = second_dist - best_dist;
×
569
    weight_term = 0U;
×
570
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
571
        delta = (int64_t)lut->weight_palette[component][second_idx]
×
572
              - (int64_t)lut->weight_palette[component][best_idx];
×
573
        weight_term += (uint64_t)(delta * delta);
×
574
    }
575
    rhs = (uint64_t)SIXEL_CERTLUT_COMPONENTS
×
576
        * (uint64_t)size * (uint64_t)size * weight_term;
×
577

578
    return delta_sq * delta_sq > rhs;
×
579
}
580

581
static uint32_t
582
sixel_certlut_pool_alloc(sixel_certlut_t *lut, int *status)
×
583
{
584
    uint32_t required;
585
    uint32_t next_capacity;
586
    uint32_t offset;
587
    uint8_t *resized;
588

589
    offset = 0U;
×
590
    if (status != NULL) {
×
591
        *status = SIXEL_FALSE;
×
592
    }
593
    required = lut->pool_size + (uint32_t)(8 * sizeof(uint32_t));
×
594
    if (required > lut->pool_capacity) {
×
595
        next_capacity = lut->pool_capacity;
×
596
        if (next_capacity == 0U) {
×
597
            next_capacity = (uint32_t)(8 * sizeof(uint32_t));
×
598
        }
599
        while (next_capacity < required) {
×
600
            if (next_capacity > UINT32_MAX / 2U) {
×
601
                return 0U;
×
602
            }
603
            next_capacity *= 2U;
×
604
        }
605
        resized = (uint8_t *)realloc(lut->pool, next_capacity);
×
606
        if (resized == NULL) {
×
607
            return 0U;
×
608
        }
609
        lut->pool = resized;
×
610
        lut->pool_capacity = next_capacity;
×
611
    }
612
    offset = lut->pool_size;
×
613
    memset(lut->pool + offset, 0, 8 * sizeof(uint32_t));
×
614
    lut->pool_size = required;
×
615
    if (status != NULL) {
×
616
        *status = SIXEL_OK;
×
617
    }
618

619
    return offset;
×
620
}
621

622
static void
623
sixel_certlut_assign_leaf(uint32_t *cell, int palette_index)
×
624
{
625
    *cell = 0x80000000U | (uint32_t)(palette_index & 0xff);
×
626
}
×
627

628
static void
629
sixel_certlut_assign_branch(uint32_t *cell, uint32_t offset)
×
630
{
631
    *cell = SIXEL_LUT_BRANCH_FLAG | (offset & 0x3fffffffU);
×
632
}
×
633

634
static int
635
sixel_certlut_palette_component(sixel_certlut_t const *lut,
×
636
                                int index, int axis)
637
{
638
    sixel_certlut_color_t const *color;
639

640
    color = &lut->palette[index];
×
641
    if (axis < 0) {
×
642
        axis = 0;
×
643
    } else if (axis >= SIXEL_CERTLUT_COMPONENTS) {
×
644
        axis = SIXEL_CERTLUT_COMPONENTS - 1;
×
645
    }
646

647
    return (int)color->comp[axis];
×
648
}
649

650
static void
651
sixel_certlut_sort_indices(sixel_certlut_t const *lut,
×
652
                           int *indices, int count, int axis)
653
{
654
    int i;
655
    int j;
656
    int key;
657
    int key_value;
658
    int current_value;
659

660
    for (i = 1; i < count; ++i) {
×
661
        key = indices[i];
×
662
        key_value = sixel_certlut_palette_component(lut, key, axis);
×
663
        j = i - 1;
×
664
        while (j >= 0) {
×
665
            current_value = sixel_certlut_palette_component(lut,
×
666
                                                            indices[j],
×
667
                                                            axis);
668
            if (current_value <= key_value) {
×
669
                break;
×
670
            }
671
            indices[j + 1] = indices[j];
×
672
            --j;
×
673
        }
674
        indices[j + 1] = key;
×
675
    }
676
}
×
677

678
static int
679
sixel_certlut_kdtree_build_recursive(sixel_certlut_t *lut,
×
680
                                     int *indices,
681
                                     int count,
682
                                     int depth)
683
{
684
    int axis;
685
    int median;
686
    int node_index;
687

688
    if (count <= 0) {
×
689
        return -1;
×
690
    }
691

692
    axis = depth % SIXEL_CERTLUT_COMPONENTS;
×
693
    sixel_certlut_sort_indices(lut, indices, count, axis);
×
694
    median = count / 2;
×
695
    node_index = lut->kdnodes_count;
×
696
    if (node_index >= lut->ncolors) {
×
697
        return -1;
×
698
    }
699
    lut->kdnodes_count++;
×
700
    lut->kdnodes[node_index].index = indices[median];
×
701
    lut->kdnodes[node_index].axis = (unsigned char)axis;
×
702
    lut->kdnodes[node_index].left =
×
703
        sixel_certlut_kdtree_build_recursive(lut,
×
704
                                             indices,
705
                                             median,
706
                                             depth + 1);
707
    lut->kdnodes[node_index].right =
×
708
        sixel_certlut_kdtree_build_recursive(lut,
×
709
                                             indices + median + 1,
×
710
                                             count - median - 1,
×
711
                                             depth + 1);
712

713
    return node_index;
×
714
}
715

716
static SIXELSTATUS
717
sixel_certlut_kdtree_build(sixel_certlut_t *lut)
×
718
{
719
    SIXELSTATUS status;
720
    int *indices;
721
    int i;
722

723
    status = SIXEL_FALSE;
×
724
    indices = NULL;
×
725
    lut->kdnodes = NULL;
×
726
    lut->kdnodes_count = 0;
×
727
    lut->kdtree_root = -1;
×
728
    if (lut->ncolors <= 0) {
×
729
        status = SIXEL_OK;
×
730
        goto end;
×
731
    }
732
    lut->kdnodes = (sixel_certlut_node_t *)
×
733
        calloc((size_t)lut->ncolors, sizeof(sixel_certlut_node_t));
×
734
    if (lut->kdnodes == NULL) {
×
735
        goto end;
×
736
    }
737
    indices = (int *)malloc((size_t)lut->ncolors * sizeof(int));
×
738
    if (indices == NULL) {
×
739
        goto end;
×
740
    }
741
    for (i = 0; i < lut->ncolors; ++i) {
×
742
        indices[i] = i;
×
743
    }
744
    lut->kdnodes_count = 0;
×
745
    lut->kdtree_root = sixel_certlut_kdtree_build_recursive(lut,
×
746
                                                            indices,
747
                                                            lut->ncolors,
748
                                                            0);
749
    if (lut->kdtree_root < 0) {
×
750
        goto end;
×
751
    }
752
    status = SIXEL_OK;
×
753

754
end:
×
755
    free(indices);
×
756
    if (SIXEL_FAILED(status)) {
×
757
        free(lut->kdnodes);
×
758
        lut->kdnodes = NULL;
×
759
        lut->kdnodes_count = 0;
×
760
        lut->kdtree_root = -1;
×
761
    }
762

763
    return status;
×
764
}
765

766
static uint64_t
767
sixel_certlut_axis_distance(sixel_certlut_t const *lut, int diff, int axis)
×
768
{
769
    uint64_t weight;
770
    uint64_t abs_diff;
771

772
    abs_diff = (uint64_t)(diff < 0 ? -diff : diff);
×
773
    if (axis < 0) {
×
774
        axis = 0;
×
775
    } else if (axis >= SIXEL_CERTLUT_COMPONENTS) {
×
776
        axis = SIXEL_CERTLUT_COMPONENTS - 1;
×
777
    }
778
    weight = lut->weights_sq[axis];
×
779

780
    return weight * abs_diff * abs_diff;
×
781
}
782

783
static void
784
sixel_certlut_consider_candidate(sixel_certlut_t const *lut,
×
785
                                 int candidate,
786
                                 int32_t const scaled_components[],
787
                                 int *best_idx,
788
                                 uint64_t *best_dist,
789
                                 int *second_idx,
790
                                 uint64_t *second_dist)
791
{
792
    uint64_t distance;
793

794
    distance = sixel_certlut_distance_precomputed(lut,
×
795
                                                  candidate,
796
                                                  scaled_components);
797
    if (distance < *best_dist) {
×
798
        *second_dist = *best_dist;
×
799
        *second_idx = *best_idx;
×
800
        *best_dist = distance;
×
801
        *best_idx = candidate;
×
802
    } else if (distance < *second_dist) {
×
803
        *second_dist = distance;
×
804
        *second_idx = candidate;
×
805
    }
806
}
×
807

808
static void
809
sixel_certlut_kdtree_search(sixel_certlut_t const *lut,
×
810
                            int node_index,
811
                            int const components[],
812
                            int32_t const scaled_components[],
813
                            int *best_idx,
814
                            uint64_t *best_dist,
815
                            int *second_idx,
816
                            uint64_t *second_dist)
817
{
818
    sixel_certlut_node_t const *node;
819
    int axis;
820
    int value;
821
    int diff;
822
    int near_child;
823
    int far_child;
824
    uint64_t axis_bound;
825
    int component_value;
826

827
    if (node_index < 0) {
×
828
        return;
×
829
    }
830
    node = &lut->kdnodes[node_index];
×
831
    sixel_certlut_consider_candidate(lut,
×
832
                                     node->index,
×
833
                                     scaled_components,
834
                                     best_idx,
835
                                     best_dist,
836
                                     second_idx,
837
                                     second_dist);
838

839
    axis = (int)node->axis;
×
840
    value = sixel_certlut_palette_component(lut, node->index, axis);
×
841
    component_value = components[axis];
×
842
    diff = component_value - value;
×
843
    if (diff <= 0) {
×
844
        near_child = node->left;
×
845
        far_child = node->right;
×
846
    } else {
847
        near_child = node->right;
×
848
        far_child = node->left;
×
849
    }
850
    if (near_child >= 0) {
×
851
        sixel_certlut_kdtree_search(lut,
×
852
                                    near_child,
853
                                    components,
854
                                    scaled_components,
855
                                    best_idx,
856
                                    best_dist,
857
                                    second_idx,
858
                                    second_dist);
859
    }
860
    axis_bound = sixel_certlut_axis_distance(lut, diff, axis);
×
861
    if (far_child >= 0 && axis_bound <= *second_dist) {
×
862
        sixel_certlut_kdtree_search(lut,
×
863
                                    far_child,
864
                                    components,
865
                                    scaled_components,
866
                                    best_idx,
867
                                    best_dist,
868
                                    second_idx,
869
                                    second_dist);
870
    }
871
}
872

873
static void
874
sixel_certlut_distance_pair(sixel_certlut_t const *lut,
×
875
                            int comp1,
876
                            int comp2,
877
                            int comp3,
878
                            int *best_idx,
879
                            int *second_idx,
880
                            uint64_t *best_dist,
881
                            uint64_t *second_dist)
882
{
883
    int i;
884
    int best_candidate;
885
    int second_candidate;
886
    uint64_t best_value;
887
    uint64_t second_value;
888
    uint64_t distance;
889
    int components[SIXEL_CERTLUT_COMPONENTS];
890
    int clamped[SIXEL_CERTLUT_COMPONENTS];
891
    int component;
892
    int32_t scaled[SIXEL_CERTLUT_COMPONENTS];
893

894
    best_candidate = (-1);
×
895
    second_candidate = (-1);
×
896
    best_value = UINT64_MAX;
×
897
    second_value = UINT64_MAX;
×
898
    components[0] = comp1;
×
899
    components[1] = comp2;
×
900
    components[2] = comp3;
×
901
    for (component = 0; component < SIXEL_CERTLUT_COMPONENTS; ++component) {
×
902
        clamped[component] = components[component];
×
903
        if (clamped[component] < 0) {
×
904
            clamped[component] = 0;
×
905
        } else if (clamped[component] > 255) {
×
906
            clamped[component] = 255;
×
907
        }
908
        scaled[component]
909
            = lut->scales[component][clamped[component]];
×
910
    }
911
    if (lut->kdnodes != NULL && lut->kdtree_root >= 0) {
×
912
        sixel_certlut_kdtree_search(lut,
×
913
                                    lut->kdtree_root,
×
914
                                    components,
915
                                    scaled,
916
                                    &best_candidate,
917
                                    &best_value,
918
                                    &second_candidate,
919
                                    &second_value);
920
    } else {
921
        for (i = 0; i < lut->ncolors; ++i) {
×
922
            distance = sixel_certlut_distance_precomputed(lut,
×
923
                                                          i,
924
                                                          scaled);
925
            if (distance < best_value) {
×
926
                second_value = best_value;
×
927
                second_candidate = best_candidate;
×
928
                best_value = distance;
×
929
                best_candidate = i;
×
930
            } else if (distance < second_value) {
×
931
                second_value = distance;
×
932
                second_candidate = i;
×
933
            }
934
        }
935
    }
936
    if (second_candidate < 0) {
×
937
        second_candidate = best_candidate;
×
938
        second_value = best_value;
×
939
    }
940
    *best_idx = best_candidate;
×
941
    *second_idx = second_candidate;
×
942
    *best_dist = best_value;
×
943
    *second_dist = second_value;
×
944
}
×
945

946
static uint8_t
947
sixel_certlut_fallback(sixel_certlut_t const *lut,
×
948
                       int comp1,
949
                       int comp2,
950
                       int comp3)
951
{
952
    int best_idx;
953
    int second_idx;
954
    uint64_t best_dist;
955
    uint64_t second_dist;
956

957
    best_idx = -1;
×
958
    second_idx = -1;
×
959
    best_dist = 0U;
×
960
    second_dist = 0U;
×
961
    if (lut == NULL) {
×
962
        return 0U;
×
963
    }
964
    /*
965
     * The lazy builder may fail when allocations run out.  Fall back to a
966
     * direct brute-force palette search so lookups still succeed even in low
967
     * memory conditions.
968
     */
969
    sixel_certlut_distance_pair(lut,
×
970
                                comp1,
971
                                comp2,
972
                                comp3,
973
                                &best_idx,
974
                                &second_idx,
975
                                &best_dist,
976
                                &second_dist);
977
    if (best_idx < 0) {
×
978
        return 0U;
×
979
    }
980

981
    return (uint8_t)best_idx;
×
982
}
983

984
SIXELSTATUS
985
sixel_certlut_build_cell(sixel_certlut_t *lut, uint32_t *cell,
×
986
                         int comp1_min,
987
                         int comp2_min,
988
                         int comp3_min,
989
                         int size)
990
{
991
    SIXELSTATUS status;
992
    int center1;
993
    int center2;
994
    int center3;
995
    int best_idx;
996
    int second_idx;
997
    uint64_t best_dist;
998
    uint64_t second_dist;
999
    uint32_t offset;
1000
    int branch_status;
1001
    uint8_t *pool_before;
1002
    size_t pool_size_before;
1003
    uint32_t cell_offset;
1004
    int cell_in_pool;
1005

1006
    if (cell == NULL || lut == NULL) {
×
1007
        status = SIXEL_BAD_ARGUMENT;
×
1008
        goto end;
×
1009
    }
1010
    if (*cell == 0U) {
×
1011
#ifdef DEBUG_LUT_TRACE
1012
        fprintf(stderr,
1013
                "build_cell comp1=%d comp2=%d comp3=%d size=%d\n",
1014
                comp1_min,
1015
                comp2_min,
1016
                comp3_min,
1017
                size);
1018
#endif
1019
    }
1020
    if (*cell != 0U) {
×
1021
        status = SIXEL_OK;
×
1022
        goto end;
×
1023
    }
1024

1025
    /*
1026
     * Each node represents an axis-aligned cube in component space.  The
1027
     * builder certifies the dominant palette index by checking the distance
1028
     * gap at the cell center.  When certification fails the cube is split into
1029
     * eight octants backed by a pool block.  Children remain unbuilt until
1030
     * lookups descend into them, keeping the workload proportional to actual
1031
     * queries.
1032
     */
1033
    status = SIXEL_FALSE;
×
1034
    sixel_certlut_cell_center(comp1_min,
×
1035
                              comp2_min,
1036
                              comp3_min,
1037
                              size,
1038
                              &center1,
1039
                              &center2,
1040
                              &center3);
1041
    sixel_certlut_distance_pair(lut,
×
1042
                                center1,
1043
                                center2,
1044
                                center3,
1045
                                &best_idx,
1046
                                &second_idx,
1047
                                &best_dist,
1048
                                &second_dist);
1049
    if (best_idx < 0) {
×
1050
        best_idx = 0;
×
1051
    }
1052
    if (size == 1) {
×
1053
        sixel_certlut_assign_leaf(cell, best_idx);
×
1054
#ifdef DEBUG_LUT_TRACE
1055
        fprintf(stderr,
1056
                "  leaf idx=%d\n",
1057
                best_idx);
1058
#endif
1059
        status = SIXEL_OK;
×
1060
        goto end;
×
1061
    }
1062
    if (sixel_certlut_is_cell_safe(lut,
×
1063
                                   best_idx,
1064
                                   second_idx,
1065
                                   size,
1066
                                   best_dist,
1067
                                   second_dist)) {
1068
        sixel_certlut_assign_leaf(cell, best_idx);
×
1069
#ifdef DEBUG_LUT_TRACE
1070
        fprintf(stderr,
1071
                "  safe leaf idx=%d\n",
1072
                best_idx);
1073
#endif
1074
        status = SIXEL_OK;
×
1075
        goto end;
×
1076
    }
1077
    pool_before = lut->pool;
×
1078
    pool_size_before = lut->pool_size;
×
1079
    cell_in_pool = 0;
×
1080
    cell_offset = 0U;
×
1081
    /*
1082
     * The pool may grow while building descendants.  Remember the caller's
1083
     * offset so the cell pointer can be refreshed after realloc moves the
1084
     * backing storage.
1085
     */
1086
    if (pool_before != NULL) {
×
1087
        if ((uint8_t *)(void *)cell >= pool_before
×
1088
                && (size_t)((uint8_t *)(void *)cell - pool_before)
×
1089
                        < pool_size_before) {
1090
            cell_in_pool = 1;
×
1091
            cell_offset = (uint32_t)((uint8_t *)(void *)cell - pool_before);
×
1092
        }
1093
    }
1094
    offset = sixel_certlut_pool_alloc(lut, &branch_status);
×
1095
    if (branch_status != SIXEL_OK) {
×
1096
        goto end;
×
1097
    }
1098
    if (cell_in_pool != 0) {
×
1099
        cell = (uint32_t *)(void *)(lut->pool + cell_offset);
×
1100
    }
1101
    sixel_certlut_assign_branch(cell, offset);
×
1102
#ifdef DEBUG_LUT_TRACE
1103
        fprintf(stderr, "  branch offset=%u\n", offset);
1104
#endif
1105
    status = SIXEL_OK;
×
1106

1107
end:
×
1108
    return status;
×
1109
}
1110

1111
SIXELSTATUS
1112
sixel_certlut_build(sixel_certlut_t *lut,
×
1113
                    sixel_certlut_color_t const *palette,
1114
                    int ncolors,
1115
                    int wcomp1,
1116
                    int wcomp2,
1117
                    int wcomp3)
1118
{
1119
    SIXELSTATUS status;
1120
    int initialized;
1121
    size_t level0_count;
1122
    status = SIXEL_FALSE;
×
1123
    initialized = sixel_certlut_init(lut);
×
1124
    if (SIXEL_FAILED(initialized)) {
×
1125
        goto end;
×
1126
    }
1127
    lut->palette = palette;
×
1128
    lut->ncolors = ncolors;
×
1129
    sixel_certlut_weight_init(lut, wcomp1, wcomp2, wcomp3);
×
1130
    status = sixel_certlut_prepare_palette_terms(lut);
×
1131
    if (SIXEL_FAILED(status)) {
×
1132
        goto end;
×
1133
    }
1134
    status = sixel_certlut_kdtree_build(lut);
×
1135
    if (SIXEL_FAILED(status)) {
×
1136
        goto end;
×
1137
    }
1138
    level0_count = (size_t)64 * (size_t)64 * (size_t)64;
×
1139
    lut->level0 = (uint32_t *)calloc(level0_count, sizeof(uint32_t));
×
1140
    if (lut->level0 == NULL) {
×
1141
        goto end;
×
1142
    }
1143
    /*
1144
     * Level 0 cells start uninitialized.  The lookup routine materializes
1145
     * individual subtrees on demand so we avoid evaluating the entire
1146
     * 64x64x64 grid upfront.
1147
     */
1148
    status = SIXEL_OK;
×
1149

1150
end:
×
1151
    if (SIXEL_FAILED(status)) {
×
1152
        sixel_certlut_release(lut);
×
1153
    }
1154
    return status;
×
1155
}
1156

1157
uint8_t
1158
sixel_certlut_lookup(sixel_certlut_t *lut,
×
1159
                     uint8_t comp1,
1160
                     uint8_t comp2,
1161
                     uint8_t comp3)
1162
{
1163
    uint32_t entry;
1164
    uint32_t offset;
1165
    uint32_t index;
1166
    uint32_t *children;
1167
    uint32_t *cell;
1168
    int shift;
1169
    int child;
1170
    int status;
1171
    int size;
1172
    int comp1_min;
1173
    int comp2_min;
1174
    int comp3_min;
1175
    int step;
1176
    if (lut == NULL || lut->level0 == NULL) {
×
1177
        return 0U;
×
1178
    }
1179
    /*
1180
     * Cells are created lazily.  A zero entry indicates an uninitialized
1181
     * subtree, so the builder is invoked with the cube bounds of the current
1182
     * traversal.  Should allocation fail we fall back to a direct brute-force
1183
     * palette search for the queried pixel.
1184
     */
1185
    index = ((uint32_t)(comp1 >> 2) << 12)
×
1186
          | ((uint32_t)(comp2 >> 2) << 6)
×
1187
          | (uint32_t)(comp3 >> 2);
×
1188
    cell = lut->level0 + index;
×
1189
    size = 4;
×
1190
    comp1_min = (int)(comp1 & 0xfc);
×
1191
    comp2_min = (int)(comp2 & 0xfc);
×
1192
    comp3_min = (int)(comp3 & 0xfc);
×
1193
    entry = *cell;
×
1194
    if (entry == 0U) {
×
1195
#ifdef DEBUG_LUT_TRACE
1196
        fprintf(stderr,
1197
                "lookup build level0 comp1=%u comp2=%u comp3=%u\n",
1198
                (unsigned int)comp1,
1199
                (unsigned int)comp2,
1200
                (unsigned int)comp3);
1201
#endif
1202
        status = sixel_certlut_build_cell(lut,
×
1203
                                          cell,
1204
                                          comp1_min,
1205
                                          comp2_min,
1206
                                          comp3_min,
1207
                                          size);
1208
        if (SIXEL_FAILED(status)) {
×
1209
            return sixel_certlut_fallback(lut,
×
1210
                                          (int)comp1,
1211
                                          (int)comp2,
1212
                                          (int)comp3);
1213
        }
1214
        entry = *cell;
×
1215
    }
1216
    shift = 1;
×
1217
    while ((entry & 0x80000000U) == 0U) {
×
1218
        offset = entry & 0x3fffffffU;
×
1219
        children = (uint32_t *)(void *)(lut->pool + offset);
×
1220
        child = (((int)(comp1 >> shift) & 1) << 2)
×
1221
              | (((int)(comp2 >> shift) & 1) << 1)
×
1222
              | ((int)(comp3 >> shift) & 1);
×
1223
#ifdef DEBUG_LUT_TRACE
1224
        fprintf(stderr,
1225
                "descend child=%d size=%d offset=%u\n",
1226
                child,
1227
                size,
1228
                offset);
1229
#endif
1230
        step = size / 2;
×
1231
        if (step <= 0) {
×
1232
            step = 1;
×
1233
        }
1234
        comp1_min += step * ((child >> 2) & 1);
×
1235
        comp2_min += step * ((child >> 1) & 1);
×
1236
        comp3_min += step * (child & 1);
×
1237
        size = step;
×
1238
        cell = children + (size_t)child;
×
1239
        entry = *cell;
×
1240
        if (entry == 0U) {
×
1241
#ifdef DEBUG_LUT_TRACE
1242
            fprintf(stderr,
1243
                    "lookup build child size=%d comp1=%d comp2=%d comp3=%d\n",
1244
                    size,
1245
                    comp1_min,
1246
                    comp2_min,
1247
                    comp3_min);
1248
#endif
1249
            status = sixel_certlut_build_cell(lut,
×
1250
                                              cell,
1251
                                              comp1_min,
1252
                                              comp2_min,
1253
                                              comp3_min,
1254
                                              size);
1255
            if (SIXEL_FAILED(status)) {
×
1256
                return sixel_certlut_fallback(lut,
×
1257
                                              (int)comp1,
1258
                                              (int)comp2,
1259
                                              (int)comp3);
1260
            }
1261
            children = (uint32_t *)(void *)(lut->pool + offset);
×
1262
            cell = children + (size_t)child;
×
1263
            entry = *cell;
×
1264
        }
1265
        if (size == 1) {
×
1266
            break;
×
1267
        }
1268
        if (shift == 0) {
×
1269
            break;
×
1270
        }
1271
        --shift;
×
1272
    }
1273

1274
    return (uint8_t)(entry & 0xffU);
×
1275
}
1276

1277
void
1278
sixel_certlut_free(sixel_certlut_t *lut)
316✔
1279
{
1280
    sixel_certlut_release(lut);
316✔
1281
    if (lut != NULL) {
316!
1282
        lut->palette = NULL;
316✔
1283
        lut->ncolors = 0;
316✔
1284
    }
1285
}
316✔
1286

1287
void
1288
sixel_lookup_8bit_init(sixel_lookup_8bit_t *lut, sixel_allocator_t *allocator)
316✔
1289
{
1290
    if (lut == NULL) {
316!
1291
        return;
×
1292
    }
1293

1294
    memset(lut, 0, sizeof(sixel_lookup_8bit_t));
316✔
1295
    lut->allocator = allocator;
316✔
1296
    lut->policy = sixel_lookup_8bit_policy_normalize(SIXEL_LUT_POLICY_6BIT);
316✔
1297
    lut->depth = 0;
316✔
1298
    lut->ncolors = 0;
316✔
1299
    lut->complexion = 1;
316✔
1300
    lut->palette = NULL;
316✔
1301
    lut->quant.channel_shift = 0U;
316✔
1302
    lut->quant.channel_bits = 8U;
316✔
1303
    lut->quant.channel_mask = 0xffU;
316✔
1304
    lut->dense = NULL;
316✔
1305
    lut->dense_size = 0U;
316✔
1306
    lut->dense_ready = 0;
316✔
1307
    lut->cert_ready = 0;
316✔
1308
    lut->cert = (sixel_certlut_t *)malloc(sizeof(sixel_certlut_t));
316✔
1309
    if (lut->cert != NULL) {
316!
1310
        sixel_certlut_init(lut->cert);
316✔
1311
    }
1312
}
1313

1314
SIXELSTATUS
1315
sixel_lookup_8bit_configure(sixel_lookup_8bit_t *lut,
640✔
1316
                            unsigned char const *palette,
1317
                            int depth,
1318
                            int ncolors,
1319
                            int complexion,
1320
                            int wcomp1,
1321
                            int wcomp2,
1322
                            int wcomp3,
1323
                            int policy,
1324
                            int pixelformat)
1325
{
1326
    SIXELSTATUS status;
1327
    int normalized;
1328

1329
    (void)pixelformat;
1330

1331
    if (lut == NULL) {
640!
1332
        return SIXEL_BAD_ARGUMENT;
×
1333
    }
1334
    if (palette == NULL) {
640!
1335
        return SIXEL_BAD_ARGUMENT;
×
1336
    }
1337
    if (depth <= 0) {
640!
1338
        return SIXEL_BAD_ARGUMENT;
×
1339
    }
1340

1341
    lut->palette = palette;
640✔
1342
    lut->depth = depth;
640✔
1343
    lut->ncolors = ncolors;
640✔
1344
    lut->complexion = complexion;
640✔
1345
    normalized = sixel_lookup_8bit_policy_normalize(policy);
640✔
1346
    lut->policy = normalized;
640✔
1347
    lut->quant = sixel_lookup_8bit_quant_make((unsigned int)depth, normalized);
640✔
1348
    lut->dense_ready = 0;
640✔
1349
    lut->cert_ready = 0;
640✔
1350

1351
    if (sixel_lookup_8bit_policy_uses_cache(normalized)) {
640!
1352
        if (depth != 3) {
640!
1353
            sixel_helper_set_additional_message(
×
1354
                "sixel_lookup_8bit_configure: fast LUT requires RGB pixels.");
1355
            return SIXEL_BAD_ARGUMENT;
×
1356
        }
1357
        status = sixel_lookup_8bit_prepare_cache(lut);
640✔
1358
        if (SIXEL_FAILED(status)) {
640!
1359
            return status;
×
1360
        }
1361
    } else {
1362
        sixel_lookup_8bit_release_cache(lut);
×
1363
        status = SIXEL_OK;
×
1364
    }
1365

1366
    if (normalized == SIXEL_LUT_POLICY_CERTLUT) {
640!
1367
        if (lut->cert == NULL) {
×
1368
            sixel_helper_set_additional_message(
×
1369
                "sixel_lookup_8bit_configure: cert buffer missing.");
1370
            return SIXEL_BAD_ALLOCATION;
×
1371
        }
1372
        status = sixel_certlut_build(lut->cert,
×
1373
                                     (sixel_certlut_color_t const *)palette,
1374
                                     ncolors,
1375
                                     wcomp1,
1376
                                     wcomp2,
1377
                                     wcomp3);
1378
        if (SIXEL_FAILED(status)) {
×
1379
            return status;
×
1380
        }
1381
        lut->cert_ready = 1;
×
1382
    }
1383

1384
    return SIXEL_OK;
640✔
1385
}
1386

1387
int
1388
sixel_lookup_8bit_map_pixel(sixel_lookup_8bit_t *lut, unsigned char const *pixel)
43,277,480✔
1389
{
1390
    if (lut == NULL || pixel == NULL) {
43,277,480!
1391
        return 0;
×
1392
    }
1393
    if (lut->policy == SIXEL_LUT_POLICY_CERTLUT) {
43,277,480!
1394
        if (!lut->cert_ready) {
×
1395
            return 0;
×
1396
        }
1397
        return (int)sixel_certlut_lookup(lut->cert,
×
1398
                                         pixel[0],
×
1399
                                         pixel[1],
×
1400
                                         pixel[2]);
×
1401
    }
1402

1403
    return sixel_lookup_8bit_lookup_fast(lut, pixel);
43,277,480✔
1404
}
1405

1406
void
1407
sixel_lookup_8bit_clear(sixel_lookup_8bit_t *lut)
316✔
1408
{
1409
    if (lut == NULL) {
316!
1410
        return;
×
1411
    }
1412

1413
    sixel_lookup_8bit_release_cache(lut);
316✔
1414
    if (lut->cert_ready && lut->cert != NULL) {
316!
1415
        sixel_certlut_free(lut->cert);
×
1416
        lut->cert_ready = 0;
×
1417
    }
1418
    lut->palette = NULL;
316✔
1419
    lut->depth = 0;
316✔
1420
    lut->ncolors = 0;
316✔
1421
    lut->complexion = 1;
316✔
1422
}
1423

1424
void
1425
sixel_lookup_8bit_finalize(sixel_lookup_8bit_t *lut)
316✔
1426
{
1427
    if (lut == NULL) {
316!
1428
        return;
×
1429
    }
1430

1431
    sixel_lookup_8bit_clear(lut);
316✔
1432
    if (lut->cert != NULL) {
316!
1433
        sixel_certlut_free(lut->cert);
316✔
1434
        free(lut->cert);
316✔
1435
        lut->cert = NULL;
316✔
1436
    }
1437
    lut->allocator = NULL;
316✔
1438
}
1439

1440
/* emacs Local Variables:      */
1441
/* emacs mode: c               */
1442
/* emacs tab-width: 4          */
1443
/* emacs indent-tabs-mode: nil */
1444
/* emacs c-basic-offset: 4     */
1445
/* emacs End:                  */
1446
/* vim: set expandtab ts=4 sts=4 sw=4 : */
1447
/* 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