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

saitoha / libsixel / 19918707358

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

push

github

saitoha
tests: fix meson msys dll lookup

9738 of 38220 branches covered (25.48%)

12841 of 33438 relevant lines covered (38.4%)

782420.02 hits per line

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

15.42
/src/lookup-float32.c
1
/*
2
 * SPDX-License-Identifier: MIT
3
 *
4
 * Copyright (c) 2025 libsixel developers. See `AUTHORS`.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24

25
/*
26
 * Float32-aware lookup backend.  The search avoids byte quantization so
27
 * floating-point inputs keep their precision when resolving palette entries.
28
 * The CERT LUT path uses a lightweight kd-tree while other policies fall back
29
 * to a linear scan over the palette.
30
 */
31

32
#include "config.h"
33

34
#include <float.h>
35
#include <math.h>
36
#include <stdlib.h>
37
#include <string.h>
38

39
#include <sixel.h>
40

41
#include "allocator.h"
42
#include "lookup-float32.h"
43
#include "pixelformat.h"
44
#include "status.h"
45

46
struct sixel_lookup_float32_node {
47
    int index;
48
    int left;
49
    int right;
50
    int axis;
51
};
52

53
static int
54
sixel_lookup_float32_policy_normalize(int policy)
×
55
{
56
    int normalized;
×
57

58
    normalized = policy;
×
59
    if (normalized == SIXEL_LUT_POLICY_AUTO) {
×
60
        normalized = SIXEL_LUT_POLICY_6BIT;
61
    } else if (normalized != SIXEL_LUT_POLICY_5BIT
×
62
               && normalized != SIXEL_LUT_POLICY_6BIT
×
63
               && normalized != SIXEL_LUT_POLICY_CERTLUT
×
64
               && normalized != SIXEL_LUT_POLICY_NONE) {
×
65
        normalized = SIXEL_LUT_POLICY_6BIT;
×
66
    }
67

68
    return normalized;
×
69
}
70

71
static float
72
sixel_lookup_float32_component(float const *palette,
×
73
                               int depth,
74
                               int index,
75
                               int axis)
76
{
77
    int clamped_axis;
×
78

79
    clamped_axis = axis;
×
80
    if (clamped_axis < 0) {
×
81
        clamped_axis = 0;
82
    } else if (clamped_axis >= SIXEL_LOOKUP_FLOAT_COMPONENTS) {
×
83
        clamped_axis = SIXEL_LOOKUP_FLOAT_COMPONENTS - 1;
84
    }
85

86
    return palette[index * depth + clamped_axis];
×
87
}
88

89
static void
90
sixel_lookup_float32_sort_indices(float const *palette,
×
91
                                  int depth,
92
                                  int *indices,
93
                                  int count,
94
                                  int axis)
95
{
96
    int i;
×
97
    int j;
×
98
    int key;
×
99
    float key_value;
×
100
    float current;
×
101

102
    for (i = 1; i < count; ++i) {
×
103
        key = indices[i];
×
104
        key_value = sixel_lookup_float32_component(palette,
×
105
                                                   depth,
106
                                                   key,
107
                                                   axis);
108
        j = i - 1;
×
109
        while (j >= 0) {
×
110
            current = sixel_lookup_float32_component(palette,
×
111
                                                     depth,
112
                                                     indices[j],
×
113
                                                     axis);
114
            if (current <= key_value) {
×
115
                break;
116
            }
117
            indices[j + 1] = indices[j];
×
118
            --j;
×
119
        }
120
        indices[j + 1] = key;
×
121
    }
122
}
×
123

124
static int
125
sixel_lookup_float32_build_kdtree(sixel_lookup_float32_t *lut,
×
126
                                  int *indices,
127
                                  int count,
128
                                  int depth)
129
{
130
    int axis;
×
131
    int median;
×
132
    int node_index;
×
133

134
    if (count <= 0) {
×
135
        return -1;
136
    }
137

138
    axis = depth % SIXEL_LOOKUP_FLOAT_COMPONENTS;
×
139
    sixel_lookup_float32_sort_indices(lut->palette,
×
140
                                      lut->depth,
141
                                      indices,
142
                                      count,
143
                                      axis);
144
    median = count / 2;
×
145
    node_index = lut->kdnodes_count;
×
146
    if (node_index >= lut->ncolors) {
×
147
        return -1;
148
    }
149

150
    lut->kdnodes_count++;
×
151
    lut->kdnodes[node_index].index = indices[median];
×
152
    lut->kdnodes[node_index].axis = axis;
×
153
    lut->kdnodes[node_index].left =
×
154
        sixel_lookup_float32_build_kdtree(lut,
×
155
                                          indices,
156
                                          median,
157
                                          depth + 1);
158
    lut->kdnodes[node_index].right =
×
159
        sixel_lookup_float32_build_kdtree(lut,
×
160
                                          indices + median + 1,
×
161
                                          count - median - 1,
×
162
                                          depth + 1);
163

164
    return node_index;
×
165
}
166

167
static float
168
sixel_lookup_float32_distance(sixel_lookup_float32_t const *lut,
×
169
                              float const *sample,
170
                              int palette_index)
171
{
172
    float diff;
×
173
    float distance;
×
174
    int component;
×
175

176
    distance = 0.0f;
×
177
    for (component = 0; component < SIXEL_LOOKUP_FLOAT_COMPONENTS;
×
178
            ++component) {
×
179
        diff = sample[component]
×
180
             - sixel_lookup_float32_component(lut->palette,
×
181
                                              lut->depth,
×
182
                                              palette_index,
183
                                              component);
184
        diff *= diff;
×
185
        diff *= (float)lut->weights[component];
×
186
        distance += diff;
×
187
    }
188

189
    return distance;
×
190
}
191

192
static void
193
sixel_lookup_float32_search_kdtree(sixel_lookup_float32_t const *lut,
×
194
                                   int node_index,
195
                                   float const *sample,
196
                                   int *best_index,
197
                                   float *best_distance)
198
{
199
    sixel_lookup_float32_node_t const *node;
×
200
    float pivot;
×
201
    float diff;
×
202
    float distance;
×
203
    float plane_distance;
×
204
    int next;
×
205
    int other;
×
206

207
    if (node_index < 0) {
×
208
        return;
209
    }
210

211
    node = &lut->kdnodes[node_index];
×
212
    pivot = sixel_lookup_float32_component(lut->palette,
×
213
                                           lut->depth,
×
214
                                           node->index,
×
215
                                           node->axis);
×
216
    diff = sample[node->axis] - pivot;
×
217
    next = node->left;
×
218
    other = node->right;
×
219
    if (diff > 0.0f) {
×
220
        next = node->right;
×
221
        other = node->left;
×
222
    }
223

224
    sixel_lookup_float32_search_kdtree(lut,
×
225
                                       next,
226
                                       sample,
227
                                       best_index,
228
                                       best_distance);
229

230
    distance = sixel_lookup_float32_distance(lut, sample, node->index);
×
231
    if (distance < *best_distance) {
×
232
        *best_distance = distance;
×
233
        *best_index = node->index;
×
234
    }
235

236
    plane_distance = diff * diff * (float)lut->weights[node->axis];
×
237
    if (plane_distance < *best_distance) {
×
238
        sixel_lookup_float32_search_kdtree(lut,
239
                                           other,
240
                                           sample,
241
                                           best_index,
242
                                           best_distance);
243
    }
244
}
×
245

246
static int
247
sixel_lookup_float32_linear_search(sixel_lookup_float32_t const *lut,
×
248
                                   float const *sample)
249
{
250
    float best;
×
251
    float distance;
×
252
    int best_index;
×
253
    int index;
×
254

255
    best = FLT_MAX;
×
256
    best_index = 0;
×
257
    for (index = 0; index < lut->ncolors; ++index) {
×
258
        distance = sixel_lookup_float32_distance(lut, sample, index);
×
259
        if (distance < best) {
×
260
            best = distance;
×
261
            best_index = index;
×
262
        }
263
    }
264

265
    return best_index;
×
266
}
267

268
void
269
sixel_lookup_float32_init(sixel_lookup_float32_t *lut,
237✔
270
                          sixel_allocator_t *allocator)
271
{
272
    if (lut == NULL) {
237!
273
        return;
274
    }
275

276
    memset(lut, 0, sizeof(sixel_lookup_float32_t));
237✔
277
    lut->policy = SIXEL_LUT_POLICY_6BIT;
237✔
278
    lut->depth = 0;
237✔
279
    lut->ncolors = 0;
237✔
280
    lut->complexion = 1;
237✔
281
    lut->weights[0] = 1;
237✔
282
    lut->weights[1] = 1;
237✔
283
    lut->weights[2] = 1;
237✔
284
    lut->palette = NULL;
237✔
285
    lut->kdnodes = NULL;
237✔
286
    lut->kdtree_root = -1;
237✔
287
    lut->kdnodes_count = 0;
237✔
288
    lut->allocator = allocator;
237✔
289
}
290

291
static void
292
sixel_lookup_float32_release_palette(sixel_lookup_float32_t *lut)
237✔
293
{
294
    if (lut->palette != NULL) {
237!
295
        sixel_allocator_free(lut->allocator, lut->palette);
×
296
        lut->palette = NULL;
×
297
    }
298
}
237✔
299

300
static void
301
sixel_lookup_float32_release_kdtree(sixel_lookup_float32_t *lut)
237✔
302
{
303
    free(lut->kdnodes);
237✔
304
    lut->kdnodes = NULL;
237✔
305
    lut->kdnodes_count = 0;
237✔
306
    lut->kdtree_root = -1;
237✔
307
}
×
308

309
void
310
sixel_lookup_float32_clear(sixel_lookup_float32_t *lut)
237✔
311
{
312
    if (lut == NULL) {
237!
313
        return;
314
    }
315

316
    sixel_lookup_float32_release_palette(lut);
237✔
317
    sixel_lookup_float32_release_kdtree(lut);
237✔
318
    lut->ncolors = 0;
237✔
319
    lut->depth = 0;
237✔
320
}
321

322
void
323
sixel_lookup_float32_finalize(sixel_lookup_float32_t *lut)
237✔
324
{
325
    if (lut == NULL) {
237!
326
        return;
327
    }
328

329
    sixel_lookup_float32_clear(lut);
237✔
330
    lut->allocator = NULL;
237✔
331
}
332

333
static SIXELSTATUS
334
sixel_lookup_float32_prepare_palette(sixel_lookup_float32_t *lut,
×
335
                                     unsigned char const *palette,
336
                                     int pixelformat)
337
{
338
    size_t total;
×
339
    int index;
×
340
    int component;
×
341
    float *cursor;
×
342

343
    total = (size_t)lut->ncolors * (size_t)lut->depth;
×
344
    sixel_lookup_float32_release_palette(lut);
×
345
    lut->palette = (float *)sixel_allocator_malloc(lut->allocator,
×
346
                                                   total * sizeof(float));
347
    if (lut->palette == NULL) {
×
348
        sixel_helper_set_additional_message(
×
349
            "sixel_lookup_float32_prepare_palette: allocation failed.");
350
        return SIXEL_BAD_ALLOCATION;
×
351
    }
352

353
    cursor = lut->palette;
354
    for (index = 0; index < lut->ncolors; ++index) {
×
355
        for (component = 0; component < lut->depth; ++component) {
×
356
            *cursor = sixel_pixelformat_byte_to_float(pixelformat,
×
357
                                                     component,
358
                                                     palette[index
×
359
                                                         * lut->depth
×
360
                                                         + component]);
×
361
            ++cursor;
×
362
        }
363
    }
364

365
    return SIXEL_OK;
366
}
367

368
static SIXELSTATUS
369
sixel_lookup_float32_prepare_kdtree(sixel_lookup_float32_t *lut)
×
370
{
371
    int *indices;
×
372
    SIXELSTATUS status;
×
373
    int i;
×
374

375
    lut->kdnodes = (sixel_lookup_float32_node_t *)
×
376
        calloc((size_t)lut->ncolors, sizeof(sixel_lookup_float32_node_t));
×
377
    if (lut->kdnodes == NULL) {
×
378
        return SIXEL_BAD_ALLOCATION;
379
    }
380

381
    indices = (int *)malloc((size_t)lut->ncolors * sizeof(int));
×
382
    if (indices == NULL) {
×
383
        free(lut->kdnodes);
×
384
        lut->kdnodes = NULL;
×
385
        return SIXEL_BAD_ALLOCATION;
×
386
    }
387

388
    for (i = 0; i < lut->ncolors; ++i) {
×
389
        indices[i] = i;
×
390
    }
391
    lut->kdnodes_count = 0;
×
392
    lut->kdtree_root = sixel_lookup_float32_build_kdtree(lut,
×
393
                                                          indices,
394
                                                          lut->ncolors,
395
                                                          0);
396
    status = SIXEL_OK;
×
397
    if (lut->kdtree_root < 0) {
×
398
        status = SIXEL_BAD_ALLOCATION;
×
399
    }
400

401
    free(indices);
×
402
    if (SIXEL_FAILED(status)) {
×
403
        sixel_lookup_float32_release_kdtree(lut);
×
404
    }
405

406
    return status;
407
}
408

409
SIXELSTATUS
410
sixel_lookup_float32_configure(sixel_lookup_float32_t *lut,
×
411
                               unsigned char const *palette,
412
                               int depth,
413
                               int ncolors,
414
                               int complexion,
415
                               int wcomp1,
416
                               int wcomp2,
417
                               int wcomp3,
418
                               int policy,
419
                               int pixelformat)
420
{
421
    SIXELSTATUS status;
×
422

423
    (void)pixelformat;
×
424

425
    if (lut == NULL || palette == NULL) {
×
426
        return SIXEL_BAD_ARGUMENT;
427
    }
428
    if (depth <= 0 || ncolors <= 0) {
×
429
        return SIXEL_BAD_ARGUMENT;
430
    }
431

432
    sixel_lookup_float32_clear(lut);
×
433
    lut->policy = sixel_lookup_float32_policy_normalize(policy);
×
434
    lut->depth = depth;
×
435
    lut->ncolors = ncolors;
×
436
    lut->complexion = complexion;
×
437
    /*
438
     * Apply per-component weighting.  The first component inherits the
439
     * complexion factor to preserve existing luminance-driven defaults while
440
     * keeping the parameter names agnostic to the color space.
441
     */
442
    lut->weights[0] = wcomp1 * complexion;
×
443
    lut->weights[1] = wcomp2;
×
444
    lut->weights[2] = wcomp3;
×
445

446
    status = sixel_lookup_float32_prepare_palette(lut, palette, pixelformat);
×
447
    if (SIXEL_FAILED(status)) {
×
448
        return status;
449
    }
450

451
    if (lut->policy == SIXEL_LUT_POLICY_CERTLUT) {
×
452
        status = sixel_lookup_float32_prepare_kdtree(lut);
×
453
        if (SIXEL_FAILED(status)) {
×
454
            return status;
455
        }
456
    }
457

458
    return SIXEL_OK;
459
}
460

461
int
462
sixel_lookup_float32_map_pixel(sixel_lookup_float32_t *lut,
×
463
                               unsigned char const *pixel)
464
{
465
    float const *sample;
×
466
    int best_index;
×
467
    float best_distance;
×
468

469
    if (lut == NULL || pixel == NULL) {
×
470
        return 0;
471
    }
472

473
    sample = (float const *)(void const *)pixel;
×
474
    if (lut->policy == SIXEL_LUT_POLICY_CERTLUT) {
×
475
        best_index = 0;
×
476
        best_distance = FLT_MAX;
×
477
        sixel_lookup_float32_search_kdtree(lut,
×
478
                                           lut->kdtree_root,
479
                                           sample,
480
                                           &best_index,
481
                                           &best_distance);
482
        return best_index;
×
483
    }
484

485
    return sixel_lookup_float32_linear_search(lut, sample);
×
486
}
487

488
/* emacs Local Variables:      */
489
/* emacs mode: c               */
490
/* emacs tab-width: 4          */
491
/* emacs indent-tabs-mode: nil */
492
/* emacs c-basic-offset: 4     */
493
/* emacs End:                  */
494
/* vim: set expandtab ts=4 sts=4 sw=4 : */
495
/* 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