1 // Copyright (c) Team CharLS.
2 // SPDX-License-Identifier: BSD-3-Clause
3 
4 #include "default_traits.h"
5 #include "encoder_strategy.h"
6 #include "jls_codec_factory.h"
7 #include "jpegls_preset_coding_parameters.h"
8 #include "lossless_traits.h"
9 #include "scan.h"
10 #include "util.h"
11 
12 #include <array>
13 #include <vector>
14 
15 namespace charls {
16 
17 using std::array;
18 using std::make_unique;
19 using std::unique_ptr;
20 using std::vector;
21 
22 namespace {
23 
24 // See JPEG-LS standard ISO/IEC 14495-1, A.3.3, golomb_code Segment A.4
quantize_gradient_org(const jpegls_pc_parameters & preset,const int32_t di)25 int8_t quantize_gradient_org(const jpegls_pc_parameters& preset, const int32_t di) noexcept
26 {
27     constexpr int32_t near_lossless{};
28 
29     if (di <= -preset.threshold3)
30         return -4;
31     if (di <= -preset.threshold2)
32         return -3;
33     if (di <= -preset.threshold1)
34         return -2;
35     if (di < -near_lossless)
36         return -1;
37     if (di <= near_lossless)
38         return 0;
39     if (di < preset.threshold1)
40         return 1;
41     if (di < preset.threshold2)
42         return 2;
43     if (di < preset.threshold3)
44         return 3;
45 
46     return 4;
47 }
48 
create_quantize_lut_lossless(const int32_t bit_count)49 vector<int8_t> create_quantize_lut_lossless(const int32_t bit_count)
50 {
51     const jpegls_pc_parameters preset{compute_default((1 << static_cast<uint32_t>(bit_count)) - 1, 0)};
52     const int32_t range{preset.maximum_sample_value + 1};
53 
54     vector<int8_t> lut(static_cast<size_t>(range) * 2);
55     for (size_t i{}; i < lut.size(); ++i)
56     {
57         lut[i] = quantize_gradient_org(preset, static_cast<int32_t>(i) - range);
58     }
59 
60     return lut;
61 }
62 
63 template<typename Strategy, typename Traits>
make_codec(const Traits & traits,const frame_info & frame_info,const coding_parameters & parameters)64 unique_ptr<Strategy> make_codec(const Traits& traits, const frame_info& frame_info, const coding_parameters& parameters)
65 {
66     return make_unique<charls::jls_codec<Traits, Strategy>>(traits, frame_info, parameters);
67 }
68 
69 } // namespace
70 
71 
72 // Lookup tables to replace code with lookup tables.
73 // To avoid threading issues, all tables are created when the program is loaded.
74 
75 // Lookup table: decode symbols that are smaller or equal to 8 bit (16 tables for each value of k)
76 // NOLINTNEXTLINE(clang-diagnostic-global-constructors)
77 const array<golomb_code_table, max_k_value> decoding_tables{
78     initialize_table(0),  initialize_table(1),  initialize_table(2),  initialize_table(3),
79     initialize_table(4),  initialize_table(5),  initialize_table(6),  initialize_table(7),
80     initialize_table(8),  initialize_table(9),  initialize_table(10), initialize_table(11),
81     initialize_table(12), initialize_table(13), initialize_table(14), initialize_table(15)};
82 
83 // Lookup tables: sample differences to bin indexes.
84 // NOLINTNEXTLINE(clang-diagnostic-global-constructors)
85 const vector<int8_t> quantization_lut_lossless_8{create_quantize_lut_lossless(8)};
86 
87 // NOLINTNEXTLINE(clang-diagnostic-global-constructors)
88 const vector<int8_t> quantization_lut_lossless_10{create_quantize_lut_lossless(10)};
89 
90 // NOLINTNEXTLINE(clang-diagnostic-global-constructors)
91 const vector<int8_t> quantization_lut_lossless_12{create_quantize_lut_lossless(12)};
92 
93 // NOLINTNEXTLINE(clang-diagnostic-global-constructors)
94 const vector<int8_t> quantization_lut_lossless_16{create_quantize_lut_lossless(16)};
95 
96 
97 template<typename Strategy>
create_codec(const frame_info & frame,const coding_parameters & parameters,const jpegls_pc_parameters & preset_coding_parameters)98 unique_ptr<Strategy> jls_codec_factory<Strategy>::create_codec(const frame_info& frame, const coding_parameters& parameters,
99                                                                const jpegls_pc_parameters& preset_coding_parameters)
100 {
101     unique_ptr<Strategy> codec;
102 
103     if (preset_coding_parameters.reset_value == 0 || preset_coding_parameters.reset_value == default_reset_value)
104     {
105         codec = try_create_optimized_codec(frame, parameters);
106     }
107 
108     if (!codec)
109     {
110         if (frame.bits_per_sample <= 8)
111         {
112             default_traits<uint8_t, uint8_t> traits(
113                 static_cast<int32_t>(calculate_maximum_sample_value(frame.bits_per_sample)), parameters.near_lossless,
114                 preset_coding_parameters.reset_value);
115             traits.maximum_sample_value = preset_coding_parameters.maximum_sample_value;
116             codec = make_unique<jls_codec<default_traits<uint8_t, uint8_t>, Strategy>>(traits, frame, parameters);
117         }
118         else
119         {
120             default_traits<uint16_t, uint16_t> traits(
121                 static_cast<int32_t>(calculate_maximum_sample_value(frame.bits_per_sample)), parameters.near_lossless,
122                 preset_coding_parameters.reset_value);
123             traits.maximum_sample_value = preset_coding_parameters.maximum_sample_value;
124             codec = make_unique<jls_codec<default_traits<uint16_t, uint16_t>, Strategy>>(traits, frame, parameters);
125         }
126     }
127 
128     codec->set_presets(preset_coding_parameters);
129     return codec;
130 }
131 
132 template<typename Strategy>
try_create_optimized_codec(const frame_info & frame,const coding_parameters & parameters)133 unique_ptr<Strategy> jls_codec_factory<Strategy>::try_create_optimized_codec(const frame_info& frame,
134                                                                          const coding_parameters& parameters)
135 {
136     if (parameters.interleave_mode == interleave_mode::sample && frame.component_count != 3 && frame.component_count != 4)
137         return nullptr;
138 
139 #ifndef DISABLE_SPECIALIZATIONS
140 
141     // optimized lossless versions common formats
142     if (parameters.near_lossless == 0)
143     {
144         if (parameters.interleave_mode == interleave_mode::sample)
145         {
146             if (frame.component_count == 3 && frame.bits_per_sample == 8)
147                 return make_codec<Strategy>(lossless_traits<triplet<uint8_t>, 8>(), frame, parameters);
148             if (frame.component_count == 4 && frame.bits_per_sample == 8)
149                 return make_codec<Strategy>(lossless_traits<quad<uint8_t>, 8>(), frame, parameters);
150         }
151         else
152         {
153             switch (frame.bits_per_sample)
154             {
155             case 8:
156                 return make_codec<Strategy>(lossless_traits<uint8_t, 8>(), frame, parameters);
157             case 12:
158                 return make_codec<Strategy>(lossless_traits<uint16_t, 12>(), frame, parameters);
159             case 16:
160                 return make_codec<Strategy>(lossless_traits<uint16_t, 16>(), frame, parameters);
161             default:
162                 break;
163             }
164         }
165     }
166 
167 #endif
168 
169     const auto maxval = static_cast<int>(calculate_maximum_sample_value(frame.bits_per_sample));
170 
171     if (frame.bits_per_sample <= 8)
172     {
173         if (parameters.interleave_mode == interleave_mode::sample)
174         {
175             if (frame.component_count == 3)
176             {
177                 return make_codec<Strategy>(default_traits<uint8_t, triplet<uint8_t>>(maxval, parameters.near_lossless),
178                                             frame, parameters);
179             }
180 
181             if (frame.component_count == 4)
182             {
183                 return make_codec<Strategy>(default_traits<uint8_t, quad<uint8_t>>(maxval, parameters.near_lossless), frame,
184                                             parameters);
185             }
186         }
187 
188         return make_codec<Strategy>(default_traits<uint8_t, uint8_t>(maxval, parameters.near_lossless), frame, parameters);
189     }
190     if (frame.bits_per_sample <= 16)
191     {
192         if (parameters.interleave_mode == interleave_mode::sample)
193         {
194             if (frame.component_count == 3)
195             {
196                 return make_codec<Strategy>(default_traits<uint16_t, triplet<uint16_t>>(maxval, parameters.near_lossless),
197                                             frame, parameters);
198             }
199 
200             if (frame.component_count == 4)
201             {
202                 return make_codec<Strategy>(default_traits<uint16_t, quad<uint16_t>>(maxval, parameters.near_lossless),
203                                             frame, parameters);
204             }
205         }
206 
207         return make_codec<Strategy>(default_traits<uint16_t, uint16_t>(maxval, parameters.near_lossless), frame, parameters);
208     }
209     return nullptr;
210 }
211 
212 
213 template class jls_codec_factory<decoder_strategy>;
214 template class jls_codec_factory<encoder_strategy>;
215 
216 } // namespace charls
217