/src/LibRaw/src/decompressors/losslessjpeg.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- C++ -*- |
2 | | * File: huffmandec.cpp |
3 | | * Copyright (C) 2024 Alex Tutubalin, LibRaw LLC |
4 | | * |
5 | | Lossless JPEG decoder |
6 | | |
7 | | Code partially backported from DNGLab's rust code |
8 | | DNGLab was originally created in 2021 by Daniel Vogelbacher. |
9 | | |
10 | | LibRaw is free software; you can redistribute it and/or modify |
11 | | it under the terms of the one of two licenses as you choose: |
12 | | |
13 | | 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1 |
14 | | (See file LICENSE.LGPL provided in LibRaw distribution archive for details). |
15 | | |
16 | | 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 |
17 | | (See file LICENSE.CDDL provided in LibRaw distribution archive for details). |
18 | | |
19 | | */ |
20 | | |
21 | | #include "../../internal/losslessjpeg.h" |
22 | | #include <string.h> |
23 | | |
24 | 0 | #define ZERO(a) do { memset(a,0,sizeof(a));} while(0) |
25 | | |
26 | | bool ByteStreamBE::skip_to_marker() // true: success, false - no marker |
27 | 0 | { |
28 | 0 | if (pos + 2 > size) return false; |
29 | 0 | while (!(buffer[pos] == 0xff && buffer[pos + 1] != 0 && buffer[pos + 1] != 0xff)) |
30 | 0 | { |
31 | 0 | pos++; |
32 | 0 | if (pos + 2 > size) |
33 | 0 | return false; |
34 | 0 | } |
35 | 0 | pos++; |
36 | 0 | return true; |
37 | 0 | } |
38 | | |
39 | 0 | LibRaw_LjpegDecompressor::LibRaw_LjpegDecompressor(uint8_t *b, unsigned bs, bool dngbug, bool csfix): buffer(b,bs), |
40 | 0 | state(State::NotInited) |
41 | 0 | { |
42 | 0 | initialize(dngbug,csfix); |
43 | 0 | } |
44 | | |
45 | 0 | LibRaw_LjpegDecompressor::LibRaw_LjpegDecompressor(uint8_t *b, unsigned bs): buffer(b,bs), |
46 | 0 | state(State::NotInited) |
47 | 0 | { |
48 | 0 | initialize(false,false); |
49 | 0 | } |
50 | | |
51 | | |
52 | | void LibRaw_LjpegDecompressor::initialize(bool dngbug, bool csfix) |
53 | 0 | { |
54 | 0 | sof.csfix = csfix; |
55 | 0 | bool dht_init[4] = { false,false,false,false }; |
56 | 0 | uint32_t dht_bits[4][17]; |
57 | 0 | uint32_t dht_huffval[4][256]; |
58 | 0 | ZERO(dht_bits); |
59 | 0 | ZERO(dht_huffval); |
60 | 0 | if (next_marker(false) != Marker::SOI) |
61 | 0 | { |
62 | 0 | state = State::NoSOI; |
63 | 0 | return; |
64 | 0 | } |
65 | 0 | while (1) |
66 | 0 | { |
67 | 0 | uint8_t marker = next_marker(true); |
68 | 0 | if (marker == Marker::SOF3) |
69 | 0 | { |
70 | 0 | if (!sof.parse_sof(buffer)) |
71 | 0 | { |
72 | 0 | state = State::InvalidSOF; |
73 | 0 | return; |
74 | 0 | } |
75 | 0 | if (sof.precision > 16 || sof.precision < 12) |
76 | 0 | { |
77 | 0 | state = State::IncorrectPrecision; |
78 | 0 | return; |
79 | 0 | } |
80 | 0 | } |
81 | 0 | else if (marker == Marker::DHT) |
82 | 0 | { |
83 | 0 | bool hres = parse_dht(dht_init, dht_bits, dht_huffval); |
84 | 0 | if(!hres) |
85 | 0 | { |
86 | 0 | state = State::InvalidDHT; |
87 | 0 | return; |
88 | 0 | } |
89 | 0 | } |
90 | 0 | else if ( marker == Marker::SOS) |
91 | 0 | { |
92 | 0 | uint32_t val = sof.parse_sos(buffer); |
93 | 0 | if (val < 0x10000) |
94 | 0 | { |
95 | 0 | predictor = (val >> 8) & 0xff; |
96 | 0 | point_transform = val & 0xff; |
97 | 0 | } |
98 | 0 | else |
99 | 0 | { |
100 | 0 | state = State::InvalidSOS; |
101 | 0 | return; |
102 | 0 | } |
103 | 0 | break; |
104 | 0 | } |
105 | 0 | else if (marker == Marker::EOI) |
106 | 0 | { |
107 | 0 | state = State::EOIReached; |
108 | 0 | return; |
109 | 0 | } |
110 | 0 | else if (marker == Marker::DQT) |
111 | 0 | { |
112 | 0 | state = State::DQTPresent; |
113 | 0 | return; |
114 | 0 | } |
115 | 0 | else if(marker == Marker::Fill) |
116 | 0 | { |
117 | 0 | state = State::EOIReached; |
118 | 0 | return; |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | 0 | dhts.resize(4); |
123 | 0 | for (int i = 0; i < 4; i++) |
124 | 0 | if (dht_init[i]) |
125 | 0 | dhts[i].initval(dht_bits[i], dht_huffval[i], dngbug); |
126 | 0 | datastart = buffer.pos; |
127 | 0 | state = State::OK; |
128 | 0 | } |
129 | | |
130 | | uint8_t LibRaw_LjpegDecompressor::next_marker(bool allowskip) |
131 | 0 | { |
132 | 0 | if (!allowskip) |
133 | 0 | { |
134 | 0 | if (buffer.get_u8() != 0xff) |
135 | 0 | return Marker::Fill; // Error; |
136 | 0 | uint8_t mark = buffer.get_u8(); |
137 | 0 | return mark; |
138 | 0 | } |
139 | 0 | if (buffer.skip_to_marker()) |
140 | 0 | return buffer.get_u8(); |
141 | 0 | else |
142 | 0 | return Marker::Fill; |
143 | 0 | } |
144 | | |
145 | | bool LibRaw_LjpegDecompressor::parse_dht(bool init[4], uint32_t bits[4][17], uint32_t huffval[4][256]) |
146 | 0 | { |
147 | 0 | uint16_t length = buffer.get_u16() - 2u; |
148 | |
|
149 | 0 | while (length > 0) |
150 | 0 | { |
151 | 0 | uint8_t b = buffer.get_u8(); |
152 | 0 | uint8_t tc = b >> 4; |
153 | 0 | uint8_t th = b & 0xf; |
154 | |
|
155 | 0 | if (tc != 0) |
156 | 0 | return false; |
157 | | |
158 | 0 | if (th > 3) |
159 | 0 | return false; |
160 | | |
161 | 0 | uint32_t acc = 0; |
162 | 0 | for (int i = 0; i < 16; i++) |
163 | 0 | { |
164 | 0 | bits[th][i + 1] = buffer.get_u8(); |
165 | 0 | acc += bits[th][i + 1]; |
166 | 0 | } |
167 | 0 | bits[th][0] = 0; |
168 | |
|
169 | 0 | if (acc > 256) |
170 | 0 | return false; |
171 | | |
172 | 0 | if (length < 1 + 16 + acc) |
173 | 0 | return false; |
174 | 0 | for (uint32_t i = 0; i < acc; i++) |
175 | 0 | huffval[th][i] = buffer.get_u8(); |
176 | |
|
177 | 0 | init[th] = true; |
178 | 0 | length -= 1 + 16 + acc; |
179 | 0 | } |
180 | 0 | return true; |
181 | 0 | } |
182 | | |
183 | | static |
184 | | #ifdef _MSC_VER |
185 | | __forceinline |
186 | | #else |
187 | | inline |
188 | | #endif |
189 | | void copy_yuv_422(uint16_t *out, uint32_t row, uint32_t col, uint32_t width, int32_t y1, int32_t y2, |
190 | | int32_t cb, int32_t cr) |
191 | 0 | { |
192 | 0 | uint32_t pix1 = row * width + col; |
193 | 0 | uint32_t pix2 = pix1 + 3; |
194 | 0 | out[pix1 + 0] = uint16_t(y1); |
195 | 0 | out[pix1 + 1] = uint16_t(cb); |
196 | 0 | out[pix1 + 2] = uint16_t(cr); |
197 | 0 | out[pix2 + 0] = uint16_t(y2); |
198 | 0 | out[pix2 + 1] = uint16_t(cb); |
199 | 0 | out[pix2 + 2] = uint16_t(cr); |
200 | 0 | } |
201 | | |
202 | | |
203 | | bool LibRaw_LjpegDecompressor::decode_ljpeg_422(std::vector<uint16_t> &_dest, int width, int height) |
204 | 0 | { |
205 | 0 | if (sof.width * 3u != unsigned(width) || sof.height != unsigned(height)) |
206 | 0 | return false; |
207 | 0 | if (width % 2 || width % 6 || height % 2) |
208 | 0 | return false; |
209 | | |
210 | 0 | if (_dest.size() < size_t(width * height)) |
211 | 0 | return false; |
212 | | |
213 | 0 | if(width < 1 || height < 1) |
214 | 0 | return false; |
215 | | |
216 | 0 | uint16_t *dest = _dest.data(); |
217 | |
|
218 | 0 | HuffTable &h1 = dhts[sof.components[0].dc_tbl]; |
219 | 0 | HuffTable &h2 = dhts[sof.components[1].dc_tbl]; |
220 | 0 | HuffTable &h3 = dhts[sof.components[2].dc_tbl]; |
221 | |
|
222 | 0 | if (!h1.initialized || !h2.initialized || !h3.initialized) |
223 | 0 | return false; |
224 | | |
225 | 0 | BitPumpJpeg pump(buffer); |
226 | |
|
227 | 0 | int32_t base = 1 << (sof.precision - point_transform - 1); |
228 | 0 | int32_t y1 = base + h1.decode(pump); |
229 | 0 | int32_t y2 = y1 + h1.decode(pump); |
230 | 0 | int32_t cb = base + h2.decode(pump); |
231 | 0 | int32_t cr = base + h3.decode(pump); |
232 | 0 | copy_yuv_422(dest, 0, 0, width, y1, y2, cb, cr); |
233 | |
|
234 | 0 | for (uint32_t row = 0; row < uint32_t(height); row++) |
235 | 0 | { |
236 | 0 | uint32_t startcol = row == 0 ? 6 : 0; |
237 | 0 | for (uint32_t col = startcol; col < uint32_t(width); col += 6) |
238 | 0 | { |
239 | 0 | uint32_t pos = (col == 0) ? (row - 1) * width : row * width + col - 3; |
240 | 0 | int32_t py = dest[pos], |
241 | 0 | pcb = dest[pos + 1], |
242 | 0 | pcr = dest[pos + 2]; |
243 | 0 | int32_t _y1 = py + h1.decode(pump); |
244 | 0 | int32_t _y2 = _y1 + h1.decode(pump); |
245 | 0 | int32_t _cb = pcb + h2.decode(pump); |
246 | 0 | int32_t _cr = pcr + h3.decode(pump); |
247 | 0 | copy_yuv_422(dest, row, col, width, _y1, _y2, _cb, _cr); |
248 | 0 | } |
249 | 0 | } |
250 | 0 | return true; |
251 | 0 | } |
252 | | |
253 | | bool LibRaw_SOFInfo::parse_sof(ByteStreamBE& input) |
254 | 0 | { |
255 | 0 | uint32_t header_length = input.get_u16(); |
256 | 0 | precision = input.get_u8(); |
257 | 0 | height = input.get_u16(); |
258 | 0 | width = input.get_u16(); |
259 | 0 | cps = input.get_u8(); |
260 | |
|
261 | 0 | if (precision > 16) |
262 | 0 | return false; |
263 | 0 | if (cps > 4 || cps < 1) |
264 | 0 | return false; |
265 | 0 | if (header_length != 8 + cps * 3) |
266 | 0 | return false; |
267 | | |
268 | 0 | components.clear(); |
269 | 0 | for (unsigned i = 0; i < cps; i++) |
270 | 0 | { |
271 | 0 | unsigned id = input.get_u8(); |
272 | 0 | unsigned subs = input.get_u8(); |
273 | 0 | components.push_back(LibRaw_JpegComponentInfo(id, i, 0, (subs >> 4), (subs & 0xf) )); |
274 | 0 | input.get_u8(); |
275 | 0 | } |
276 | 0 | return true; |
277 | 0 | } |
278 | | |
279 | | uint32_t LibRaw_SOFInfo::parse_sos(ByteStreamBE& input) |
280 | 0 | { |
281 | 0 | if (width == 0) |
282 | 0 | return 0x10000; |
283 | | |
284 | 0 | input.get_u16(); |
285 | |
|
286 | 0 | uint32_t soscps = input.get_u8(); |
287 | 0 | if (cps != soscps) |
288 | 0 | return 0x10000; |
289 | 0 | for (uint32_t csi = 0; csi < cps; csi++) |
290 | 0 | { |
291 | 0 | uint32_t readcs = input.get_u8(); |
292 | 0 | uint32_t cs = csfix ? csi : readcs; // csfix might be used in MOS decoder |
293 | 0 | int cid = -1; |
294 | 0 | for(unsigned c = 0; c < components.size(); c++) |
295 | 0 | if (components[c].id == cs) |
296 | 0 | { |
297 | 0 | cid = c; |
298 | 0 | break; |
299 | 0 | } |
300 | 0 | if (cid < 0) |
301 | 0 | return 0x10000; |
302 | | |
303 | 0 | uint8_t td = input.get_u8() >> 4; |
304 | 0 | if (td > 3) |
305 | 0 | return 0x10000; |
306 | 0 | components[cid].dc_tbl = td; |
307 | 0 | } |
308 | 0 | uint8_t pred = input.get_u8(); |
309 | 0 | input.get_u8(); |
310 | 0 | uint8_t pt = input.get_u8() & 0xf; |
311 | 0 | return (pred << 8 | pt); |
312 | 0 | } |
313 | | |
314 | | HuffTable::HuffTable() |
315 | 0 | { |
316 | 0 | ZERO(bits); |
317 | 0 | ZERO(huffval); |
318 | 0 | ZERO(shiftval); |
319 | 0 | dng_bug = false; |
320 | 0 | disable_cache = false; |
321 | 0 | nbits = 0; |
322 | 0 | initialized = false; |
323 | 0 | } |
324 | | |
325 | | struct PseudoPump : public BitPump |
326 | | { |
327 | | uint64_t bits; |
328 | | int32_t nbits; |
329 | 0 | PseudoPump() : bits(0), nbits(0) {} |
330 | | |
331 | | void set(uint32_t _bits, uint32_t nb) |
332 | 0 | { |
333 | 0 | bits = uint64_t(_bits) << 32; |
334 | 0 | nbits = nb + 32; |
335 | 0 | } |
336 | 0 | int32_t valid() { return nbits - 32; } |
337 | | |
338 | | uint32_t peek(uint32_t num) |
339 | 0 | { |
340 | 0 | return uint32_t((bits >> (nbits - num)) & 0xffffffffUL); |
341 | 0 | } |
342 | | |
343 | | void consume(uint32_t num) |
344 | 0 | { |
345 | 0 | nbits -= num; |
346 | 0 | bits &= (uint64_t(1) << nbits) - 1UL; |
347 | 0 | } |
348 | | }; |
349 | | |
350 | | void HuffTable::initval(uint32_t _bits[17], uint32_t _huffval[256], bool _dng_bug) |
351 | 0 | { |
352 | 0 | memmove(bits, _bits, sizeof(bits)); |
353 | 0 | memmove(huffval, _huffval, sizeof(huffval)); |
354 | 0 | dng_bug = _dng_bug; |
355 | |
|
356 | 0 | nbits = 16; |
357 | 0 | for(int i = 0; i < 16; i++) |
358 | 0 | { |
359 | 0 | if(bits[16 - i] != 0) |
360 | 0 | break; |
361 | 0 | nbits--; |
362 | 0 | } |
363 | 0 | hufftable.resize( size_t(1ULL << nbits)); |
364 | 0 | for (unsigned i = 0; i < hufftable.size(); i++) hufftable[i] = 0; |
365 | |
|
366 | 0 | int h = 0; |
367 | 0 | int pos = 0; |
368 | 0 | for (uint8_t len = 0; len < nbits; len++) |
369 | 0 | { |
370 | 0 | for (uint32_t i = 0; i < bits[len + 1]; i++) |
371 | 0 | { |
372 | 0 | for (int j = 0; j < (1 << (nbits - len - 1)); j++) |
373 | 0 | { |
374 | 0 | hufftable[h] = ((len+1) << 16) | (uint8_t(huffval[pos] & 0xff) << 8) | uint8_t(shiftval[pos] & 0xff); |
375 | 0 | h++; |
376 | 0 | } |
377 | 0 | pos++; |
378 | 0 | } |
379 | 0 | } |
380 | 0 | if (!disable_cache) |
381 | 0 | { |
382 | 0 | PseudoPump pump; |
383 | 0 | decodecache = std::vector<uint64_t>(1 << LIBRAW_DECODE_CACHE_BITS,0); |
384 | 0 | for(uint32_t i = 0; i < 1 << LIBRAW_DECODE_CACHE_BITS; i++) |
385 | 0 | { |
386 | 0 | pump.set(i, LIBRAW_DECODE_CACHE_BITS); |
387 | 0 | uint32_t len; |
388 | 0 | int16_t val16 = int16_t(decode_slow2(pump,len)); |
389 | 0 | if (pump.valid() >= 0) |
390 | 0 | decodecache[i] = LIBRAW_CACHE_PRESENT_FLAG | uint64_t(((len & 0xff) << 16) | uint16_t(val16)); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | initialized = true; |
394 | 0 | } |