charls-sys 2.4.5

Bindings for charls
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
// Copyright (c) Team CharLS.
// SPDX-License-Identifier: BSD-3-Clause

#pragma once

#include "charls/annotations.h"
#include "charls/jpegls_error.h"

#include "byte_span.h"
#include "constants.h"

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <type_traits>


// Use an uppercase alias for assert to make it clear that ASSERT is a pre-processor macro.
#ifdef _MSC_VER
// C26493 = Don't use C-style casts
#define ASSERT(expression) \
    __pragma(warning(push)) __pragma(warning(disable : 26493)) assert(expression) __pragma(warning(pop))
#else
#define ASSERT(expression) assert(expression)
#endif

// Use forced inline for supported C++ compilers in release builds.
// Note: usage of FORCE_INLINE may be reduced in the future as the latest generation of C++ compilers
// can handle optimization by themselves.
#ifndef FORCE_INLINE
#ifdef NDEBUG
#ifdef _MSC_VER
#define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
// C++ Compilers that support the GCC extensions (GCC, clang, Intel, etc.)
#define FORCE_INLINE __attribute__((always_inline))
#else
// Unknown C++ compiler, fallback to auto inline.
#define FORCE_INLINE
#endif
#else
// Do not force inline in debug builds.
#define FORCE_INLINE
#endif
#endif


#ifdef _MSC_VER
#define MSVC_WARNING_SUPPRESS(x) \
    __pragma(warning(push)) __pragma(warning(disable : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses)
#define MSVC_WARNING_UNSUPPRESS() __pragma(warning(pop))

#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) \
    __pragma(warning(suppress \
                     : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses, cppcoreguidelines-macro-usage)

// Helper macro for SAL annotations.
#define USE_DECL_ANNOTATIONS _Use_decl_annotations_

#else
#define MSVC_WARNING_SUPPRESS(x)
#define MSVC_WARNING_UNSUPPRESS()
#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x)
#define USE_DECL_ANNOTATIONS
#endif

// C++20 has support for [[likely]] and [[unlikely]]. Use for now the GCC\Clang extension.
// MSVC has in C++14\C++17 mode no alternative for it.
#ifdef __GNUC__
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define UNLIKELY(x) (x)
#endif

// C++20 provides std::endian, use for now compiler macros.
#ifdef _MSC_VER
#define LITTLE_ENDIAN_ARCHITECTURE // MSVC++ compiler support only little endian platforms.
#elif __GNUC__
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define LITTLE_ENDIAN_ARCHITECTURE
#endif
#else
#error "Unknown compiler"
#endif

// Turn A into a string literal without expanding macro definitions
// (however, if invoked from a macro, macro arguments are expanded).
#define TO_STRING_NX(A) #A // NOLINT(cppcoreguidelines-macro-usage)

// Turn A into a string literal after macro-expanding it.
#define TO_STRING(A) TO_STRING_NX(A) // NOLINT(cppcoreguidelines-macro-usage)


namespace charls {

inline jpegls_errc to_jpegls_errc() noexcept
{
    try
    {
        // re-trow the exception.
        throw;
    }
    catch (const jpegls_error& error)
    {
        return static_cast<jpegls_errc>(error.code().value());
    }
    catch (const std::bad_alloc&)
    {
        return jpegls_errc::not_enough_memory;
    }
    catch (...)
    {
        return jpegls_errc::unexpected_failure;
    }
}

inline void clear_error_message(CHARLS_OUT_OPT char* error_message) noexcept
{
    if (error_message)
    {
        error_message[0] = 0;
    }
}


/// <summary>
/// Cross platform safe version of strcpy.
/// </summary>
inline void string_copy(CHARLS_IN_Z const char* source, CHARLS_OUT_WRITES_Z(size_in_bytes) char* destination,
                        const size_t size_in_bytes) noexcept
{
    ASSERT(strlen(source) < size_in_bytes && "String will be truncated");

#if defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__) && __STDC_WANT_SECURE_LIB__ == 1
    constexpr size_t truncate{static_cast<size_t>(-1)};
    strncpy_s(destination, size_in_bytes, source, truncate);
#else
    strncpy(destination, source, size_in_bytes);
    destination[size_in_bytes - 1] = 0;
#endif
}

inline jpegls_errc set_error_message(const jpegls_errc error,
                                     CHARLS_OUT_WRITES_Z(ErrorMessageSize) char* error_message) noexcept
{
    if (error_message)
    {
        string_copy(charls_get_error_message(error), error_message, ErrorMessageSize);
    }

    return error;
}


/// <Remarks>
/// Bits per sample can be computed if range is passed as argument.
/// </Remarks>
CHARLS_CONSTEXPR int32_t log2_ceil(const int32_t n) noexcept
{
    ASSERT(n >= 0);
    ASSERT(static_cast<uint32_t>(n) <= std::numeric_limits<uint32_t>::max() >> 2); // otherwise 1 << x becomes negative.

    int32_t x{};
    while (n > (1 << x))
    {
        ++x;
    }
    return x;
}


constexpr int32_t sign(const int32_t n) noexcept
{
    return (n >> (int32_t_bit_count - 1)) | 1;
}


constexpr int32_t bit_wise_sign(const int32_t i) noexcept
{
    return i >> (int32_t_bit_count - 1);
}


/// <summary>
/// Computes the parameter RANGE. When NEAR = 0, RANGE = MAXVAL + 1. (see ISO/IEC 14495-1, A.2.1)
/// </summary>
constexpr int32_t compute_range_parameter(const int32_t maximum_sample_value, const int32_t near_lossless) noexcept
{
    return (maximum_sample_value + 2 * near_lossless) / (2 * near_lossless + 1) + 1;
}


/// <summary>
/// Computes the parameter LIMIT. (see ISO/IEC 14495-1, A.2.1)
/// </summary>
constexpr int32_t compute_limit_parameter(const int32_t bits_per_pixel)
{
    return 2 * (bits_per_pixel + std::max(8, bits_per_pixel));
}


template<typename SampleType>
struct triplet
{
    triplet() noexcept : v1{}, v2{}, v3{}
    {
    }

    triplet(int32_t x1, int32_t x2, int32_t x3) noexcept :
        v1(static_cast<SampleType>(x1)), v2(static_cast<SampleType>(x2)), v3(static_cast<SampleType>(x3))
    {
    }

    union
    {
        SampleType v1;
        SampleType R;
    };
    union
    {
        SampleType v2;
        SampleType G;
    };
    union
    {
        SampleType v3;
        SampleType B;
    };
};


inline bool operator==(const triplet<uint8_t>& lhs, const triplet<uint8_t>& rhs) noexcept
{
    return lhs.v1 == rhs.v1 && lhs.v2 == rhs.v2 && lhs.v3 == rhs.v3;
}


inline bool operator!=(const triplet<uint8_t>& lhs, const triplet<uint8_t>& rhs) noexcept
{
    return !(lhs == rhs);
}


template<typename SampleType>
struct quad final : triplet<SampleType>
{
    MSVC_WARNING_SUPPRESS(26495) // false warning that v4 is uninitialized [VS 2017 15.9.4]
    quad() noexcept : triplet<SampleType>(), v4{}
    {
    }
    MSVC_WARNING_UNSUPPRESS()

    MSVC_WARNING_SUPPRESS(26495) // false warning that v4 is uninitialized [VS 2017 15.9.4]
    quad(triplet<SampleType> triplet_value, int32_t alpha) noexcept :
        triplet<SampleType>(triplet_value), A(static_cast<SampleType>(alpha))
    {
    }
    MSVC_WARNING_UNSUPPRESS()

    union
    {
        SampleType v4;
        SampleType A;
    };
};


template<typename Callback>
struct callback_function final
{
    Callback handler;
    void* user_context;
};


// C++23 comes with std::byteswap. Use our own byte_swap implementation for now.

// A simple overload with uint64_t\uint32_t doesn't work for macOS. size_t is not the same type as uint64_t.
template<int Bits, typename T>
constexpr bool is_uint_v = sizeof(T) == (Bits / 8) && std::is_integral<T>::value && !std::is_signed<T>::value;

template<typename T>
CHARLS_CHECK_RETURN auto byte_swap(const T value) noexcept -> std::enable_if_t<is_uint_v<16, T>, uint16_t>
{
#ifdef _MSC_VER
    return _byteswap_ushort(value);
#else
    // Note: GCC and Clang will optimize this pattern to a built-in intrinsic.
    return static_cast<uint16_t>(value << 8 | value >> 8);
#endif
}

template<typename T>
CHARLS_CHECK_RETURN auto byte_swap(const T value) noexcept -> std::enable_if_t<is_uint_v<32, T>, uint32_t>
{
#ifdef _MSC_VER
    return _byteswap_ulong(value);
#else
    // Note: GCC and Clang will optimize this pattern to a built-in intrinsic.
    return value >> 24 | (value & 0x00FF0000) >> 8 | (value & 0x0000FF00) << 8 | value << 24;
#endif
}

template<typename T>
CHARLS_CHECK_RETURN auto byte_swap(const T value) noexcept -> std::enable_if_t<is_uint_v<64, T>, uint64_t>
{
#ifdef _MSC_VER
    return _byteswap_uint64(value);
#else
    // Note: GCC and Clang will optimize this pattern to a built-in intrinsic.
    return (value << 56) | ((value << 40) & 0x00FF'0000'0000'0000) | ((value << 24) & 0x0000'FF00'0000'0000) |
           ((value << 8) & 0x0000'00FF'0000'0000) | ((value >> 8) & 0x0000'0000'FF00'0000) |
           ((value >> 24) & 0x0000'0000'00FF'0000) | ((value >> 40) & 0x0000'0000'0000'FF00) | (value >> 56);
#endif
}


template<typename T>
T read_unaligned(const void* buffer) noexcept
{
    // Note: MSVC, GCC and clang will replace this with a direct register read if the CPU architecture allows it
    // On x86, x64 and ARM64 this will just be 1 register load.
    T value;
    memcpy(&value, buffer, sizeof(T));
    return value;
}

#ifdef __EMSCRIPTEN__

// Note: WebAssembly (emcc 3.1.1) will fail with the default read_unaligned.

template<typename T>
T read_big_endian_unaligned(const void* /*buffer*/) noexcept;

template<>
inline uint16_t read_big_endian_unaligned<uint16_t>(const void* buffer) noexcept
{
    const uint8_t* p{static_cast<const uint8_t*>(buffer)};
    return (static_cast<uint32_t>(p[0]) << 8U) + static_cast<uint32_t>(p[1]);
}

template<>
inline uint32_t read_big_endian_unaligned<uint32_t>(const void* buffer) noexcept
{
    const uint8_t* p{static_cast<const uint8_t*>(buffer)};
    return (static_cast<uint32_t>(p[0]) << 24U) + (static_cast<uint32_t>(p[1]) << 16U) +
           (static_cast<uint32_t>(p[2]) << 8U) + static_cast<uint32_t>(p[3]);
}

template<>
inline size_t read_big_endian_unaligned<size_t>(const void* buffer) noexcept
{
    static_assert(sizeof(size_t) == sizeof(uint32_t), "wasm32 only");
    return read_big_endian_unaligned<uint32_t>(buffer);
}

#else

template<typename T>
T read_big_endian_unaligned(const void* buffer) noexcept
{
#ifdef LITTLE_ENDIAN_ARCHITECTURE
    return byte_swap(read_unaligned<T>(buffer));
#else
    return read_unaligned<T>(buffer);
#endif
}

#endif


inline void skip_bytes(byte_span& stream_info, const size_t count) noexcept
{
    ASSERT(count <= stream_info.size);

    stream_info.data += count;
    stream_info.size -= count;
}


template<typename T>
T* check_pointer(T* pointer)
{
    if (UNLIKELY(!pointer))
        impl::throw_jpegls_error(jpegls_errc::invalid_argument);

    return pointer;
}


/// <summary>
/// Validates the boolean 'expression'.
/// </summary>
/// <exception cref="charls::jpegls_error">Throws jpegls_errc::invalid_operation if 'expression' is false.</exception>
inline void check_operation(const bool expression)
{
    if (UNLIKELY(!expression))
        impl::throw_jpegls_error(jpegls_errc::invalid_operation);
}


/// <summary>
/// Validates the boolean 'expression'.
/// </summary>
/// <exception cref="charls::jpegls_error">Throws jpegls_errc if 'expression' is false.</exception>
inline void check_argument(const bool expression, const jpegls_errc error_value = jpegls_errc::invalid_argument)
{
    if (UNLIKELY(!expression))
        impl::throw_jpegls_error(error_value);
}


inline void check_interleave_mode(const charls::interleave_mode mode, const jpegls_errc error_value)
{
    if (UNLIKELY(!(mode == interleave_mode::none || mode == interleave_mode::line || mode == interleave_mode::sample)))
        impl::throw_jpegls_error(error_value);
}


CHARLS_CONSTEXPR int32_t calculate_maximum_sample_value(const int32_t bits_per_sample)
{
    ASSERT(bits_per_sample > 0 && bits_per_sample <= 16);
    return static_cast<int32_t>((1U << bits_per_sample) - 1);
}


/// <summary>
/// Computes how many bytes are needed to hold the number of bits.
/// </summary>
constexpr uint32_t bit_to_byte_count(const int32_t bit_count) noexcept
{
    return static_cast<uint32_t>((bit_count + 7) / 8);
}


/// <summary>
/// Converts an enumeration to its underlying type. Equivalent to C++23 std::to_underlying
/// </summary>
template<typename Enum>
constexpr auto to_underlying_type(Enum e) noexcept
{
    return static_cast<std::underlying_type_t<Enum>>(e);
}

#ifdef _MSC_VER
#if defined(_M_X64) || defined(_M_ARM64)
/// <summary>
/// Custom implementation of C++20 std::countl_zero (for uint64_t)
/// </summary>
inline int countl_zero(const uint64_t value) noexcept
{
    if (value == 0)
        return 64;

    unsigned long index;
    _BitScanReverse64(&index, value);
    return static_cast<int>(63U - index);
}
#endif

/// <summary>
/// Custom implementation of C++20 std::countl_zero (for uint32_t)
/// </summary>
inline int countl_zero(const uint32_t value) noexcept
{
    if (value == 0)
        return 32;

    unsigned long index;
    _BitScanReverse(&index, value);

    return static_cast<int>(31U - index);
}
#endif

#ifdef __GNUC__

// A simple overload with uint64_t\uint32_t doesn't work for macOS. size_t is not the same type as uint64_t.

/// <summary>
/// Custom implementation of C++20 std::countl_zero (for uint64_t)
/// </summary>
template<typename T>
auto countl_zero(const T value) noexcept -> std::enable_if_t<is_uint_v<64, T>, int>
{
    if (value == 0)
        return 64;

    return __builtin_clzll(value);
}

/// <summary>
/// Custom implementation of C++20 std::countl_zero (for uint32_t)
/// </summary>
template<typename T>
auto countl_zero(const T value) noexcept -> std::enable_if_t<is_uint_v<32, T>, int>
{
    if (value == 0)
        return 32;

    return __builtin_clz(value);
}

#endif


#if INTPTR_MAX == INT64_MAX
constexpr size_t checked_mul(const size_t a, const size_t b) noexcept
{
    return a * b;
}
#elif INTPTR_MAX == INT32_MAX
inline size_t checked_mul(const size_t a, const size_t b)
{
    const size_t result{a * b};
    if (UNLIKELY(result < a || result < b)) // check for unsigned integer overflow.
        impl::throw_jpegls_error(jpegls_errc::parameter_value_not_supported);
    return result;
}
#endif


} // namespace charls