/src/libraw/internal/losslessjpeg.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- C++ -*- |
2 | | * File: huffmandec.h |
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 | | #pragma once |
21 | | #include <stdint.h> |
22 | | #include <vector> |
23 | | |
24 | | struct BitPump // generic bit source |
25 | | { |
26 | | virtual uint32_t peek(uint32_t num) = 0; |
27 | | virtual void consume(uint32_t num) = 0; |
28 | | |
29 | | uint32_t get(uint32_t num) |
30 | 0 | { |
31 | 0 | if(num == 0) { return 0u; } |
32 | 0 | uint32_t val = peek(num); |
33 | 0 | consume(num); |
34 | 0 | return val; |
35 | 0 | } |
36 | | }; |
37 | | |
38 | | struct ByteStreamBE // Jpeg is always big endian |
39 | | { |
40 | | enum Exceptions |
41 | | { |
42 | | OK = 0, |
43 | | EndOfBuffer = 1 |
44 | | }; |
45 | | |
46 | | uint8_t *buffer; |
47 | | unsigned size, pos; |
48 | 0 | ByteStreamBE(uint8_t *b, unsigned s) : buffer(b), size(s), pos(0) {} |
49 | 0 | ByteStreamBE() : buffer(0),size(0),pos(0){} |
50 | | bool skip_to_marker(); |
51 | | |
52 | | uint8_t get_u8() |
53 | 0 | { |
54 | 0 | if (pos >= size) |
55 | 0 | throw EndOfBuffer; |
56 | 0 | uint8_t ret = buffer[pos]; |
57 | 0 | pos++; |
58 | 0 | return ret; |
59 | 0 | } |
60 | | uint16_t get_u16() |
61 | 0 | { |
62 | 0 | if (pos + 2 > size) |
63 | 0 | throw EndOfBuffer; |
64 | 0 | uint8_t r1 = buffer[pos]; |
65 | 0 | uint8_t r2 = buffer[pos + 1]; |
66 | 0 | pos += 2; |
67 | 0 | return (r1 << 8) | r2; |
68 | 0 | } |
69 | | }; |
70 | | |
71 | | struct BitPumpJpeg : public BitPump |
72 | | { |
73 | | uint8_t *buffer; |
74 | | unsigned size, pos; |
75 | | uint64_t bits; |
76 | | uint32_t nbits; |
77 | | bool finished; |
78 | | |
79 | | void consume(uint32_t num) |
80 | 0 | { |
81 | 0 | if (num <= nbits) |
82 | 0 | { |
83 | 0 | nbits -= num; |
84 | 0 | bits &= (uint64_t(1) << nbits) - 1UL; |
85 | 0 | } |
86 | 0 | } |
87 | | uint32_t peek(uint32_t num) |
88 | 0 | { |
89 | 0 | if (num > nbits && !finished) |
90 | 0 | { |
91 | 0 | if ((size >= 4) && pos < size && buffer[pos] != 0xff && buffer[pos + 1] != 0xff && buffer[pos + 2] != 0xff && |
92 | 0 | buffer[pos + 3] != 0xff) |
93 | 0 | { |
94 | 0 | uint64_t inbits = (uint32_t(buffer[pos]) << 24) | (uint32_t(buffer[pos + 1]) << 16) | |
95 | 0 | (uint32_t(buffer[pos + 2]) << 8) | (uint32_t(buffer[pos + 3])); |
96 | 0 | bits = (bits << 32) | inbits; |
97 | 0 | pos += 4; |
98 | 0 | nbits += 32; |
99 | 0 | } |
100 | 0 | else |
101 | 0 | { |
102 | 0 | int read_bytes = 0; |
103 | 0 | while (read_bytes < 4 && !finished) |
104 | 0 | { |
105 | 0 | uint8_t byte = 0; |
106 | 0 | if (pos >= size) |
107 | 0 | finished = true; |
108 | 0 | else |
109 | 0 | { |
110 | 0 | uint8_t nextbyte = buffer[pos]; |
111 | 0 | if (nextbyte != 0xff) |
112 | 0 | byte = nextbyte; |
113 | 0 | else if (buffer[pos + 1] == 0x00) |
114 | 0 | { |
115 | 0 | pos += 1; |
116 | 0 | byte = nextbyte; |
117 | 0 | } |
118 | 0 | else |
119 | 0 | finished = true; |
120 | 0 | }; |
121 | 0 | bits = (bits << 8) | uint64_t(byte); |
122 | 0 | pos += 1; |
123 | 0 | nbits += 8; |
124 | 0 | read_bytes += 1; |
125 | 0 | } |
126 | 0 | } |
127 | 0 | } |
128 | 0 | if(num > nbits && finished) |
129 | 0 | { |
130 | 0 | bits <<= 32; |
131 | 0 | nbits += 32; |
132 | 0 | } |
133 | 0 | return uint32_t(bits >> (nbits - num)); |
134 | 0 | } |
135 | | |
136 | 0 | BitPumpJpeg(ByteStreamBE& s): buffer(s.buffer+s.pos),size(s.size-s.pos),pos(0),bits(0),nbits(0),finished(false){} |
137 | | }; |
138 | | |
139 | | |
140 | | const uint32_t LIBRAW_DECODE_CACHE_BITS = 13; |
141 | | const uint64_t LIBRAW_CACHE_PRESENT_FLAG = 0x100000000L; |
142 | | |
143 | | struct HuffTable |
144 | | { |
145 | | uint32_t bits[17]; |
146 | | uint32_t huffval[256]; |
147 | | uint32_t shiftval[256]; |
148 | | bool dng_bug; |
149 | | bool disable_cache; |
150 | | uint32_t nbits; |
151 | | std::vector<uint32_t> hufftable; // len:8 << 16| huffval:8 << 8 | shift:8 |
152 | | std::vector<uint64_t> decodecache; |
153 | | bool initialized; |
154 | | HuffTable(); |
155 | | void initval(uint32_t bits[17], uint32_t huffval[256], bool dng_bug); |
156 | | |
157 | | int32_t decode(BitPump& pump) |
158 | 0 | { |
159 | 0 | uint64_t cached = disable_cache ? 0 : decodecache[pump.peek(LIBRAW_DECODE_CACHE_BITS)]; |
160 | 0 | if (cached & LIBRAW_CACHE_PRESENT_FLAG) |
161 | 0 | { |
162 | 0 | uint32_t _bits = (cached >> 16) & 0xff; |
163 | 0 | int16_t val = int16_t(cached & 0xffff); |
164 | 0 | if (val == -32768 && dng_bug) |
165 | 0 | { |
166 | 0 | if (_bits > 16) |
167 | 0 | pump.consume(_bits - 16); |
168 | 0 | } |
169 | 0 | else |
170 | 0 | pump.consume(_bits); |
171 | 0 | return val; |
172 | 0 | } |
173 | 0 | else |
174 | 0 | return decode_slow1(pump); |
175 | 0 | } |
176 | | |
177 | | int32_t decode_slow1(BitPump &pump) |
178 | 0 | { |
179 | 0 | int32_t _diff = diff(pump, len(pump)); |
180 | 0 | return _diff; |
181 | 0 | } |
182 | | |
183 | | int32_t decode_slow2(BitPump & pump, uint32_t& outlen) // output: (len+shift):8, code:16 |
184 | 0 | { |
185 | 0 | uint32_t _len = len(pump); |
186 | 0 | int32_t _diff = diff(pump, _len); |
187 | 0 | uint8_t bits8 = (_len >> 16) & 0xff; |
188 | 0 | uint8_t len8 = (_len >> 8) & 0xff; |
189 | 0 | outlen = bits8 + len8; |
190 | 0 | return _diff; |
191 | 0 | } |
192 | | |
193 | | uint32_t len(BitPump & pump) //bits:8, len:8, shift:8 |
194 | 0 | { |
195 | 0 | uint32_t code = pump.peek(nbits); |
196 | 0 | uint32_t huffdata = hufftable[code]; |
197 | 0 | uint32_t _bits = (huffdata >> 16) & 0xff; |
198 | 0 | pump.consume(_bits); |
199 | 0 | return huffdata; |
200 | 0 | } |
201 | | |
202 | | int32_t diff(BitPump & pump, uint32_t hentry) // input: bits:8, len:8, shift:8; output: diff:i32 |
203 | 0 | { |
204 | 0 | uint32_t len = (hentry >> 8) & 0xff; |
205 | 0 | if (len == 0) |
206 | 0 | return 0; |
207 | 0 | if (len == 16) |
208 | 0 | { |
209 | 0 | if (dng_bug) |
210 | 0 | pump.get(16); |
211 | 0 | return -32768; |
212 | 0 | } |
213 | 0 | uint32_t shift = hentry & 0xff; |
214 | 0 | uint32_t fulllen = len + shift; |
215 | 0 | uint32_t _bits = pump.get(len); |
216 | 0 | int32_t diff = ((_bits << 1) + 1) << shift >> 1; |
217 | 0 | if ((diff & (1 << (fulllen - 1))) == 0) |
218 | 0 | { |
219 | 0 | diff -= int32_t((1 << fulllen) - ((shift == 0))); |
220 | 0 | } |
221 | 0 | return diff; |
222 | 0 | } |
223 | | }; |
224 | | |
225 | | struct LibRaw_JpegComponentInfo |
226 | | { |
227 | | unsigned id; |
228 | | unsigned index; |
229 | | unsigned dc_tbl; |
230 | | unsigned subsample_h, subsample_v; |
231 | 0 | LibRaw_JpegComponentInfo() : id(0), index(0), dc_tbl(0), subsample_h(0), subsample_v(0) {}; |
232 | | LibRaw_JpegComponentInfo(unsigned _id, unsigned _index, unsigned _dcn, unsigned _sh, unsigned _sv) |
233 | 0 | : id(_id), index(_index), dc_tbl(_dcn), subsample_h(_sh), subsample_v(_sv){}; |
234 | 0 | LibRaw_JpegComponentInfo(const LibRaw_JpegComponentInfo& q): id(q.id), index(q.index), dc_tbl(q.dc_tbl), |
235 | 0 | subsample_h(q.subsample_h),subsample_v(q.subsample_v){} |
236 | | }; |
237 | | |
238 | | struct LibRaw_SOFInfo |
239 | | { |
240 | | unsigned width, height; |
241 | | unsigned cps, precision; |
242 | | std::vector<LibRaw_JpegComponentInfo> components; |
243 | | bool csfix; |
244 | 0 | LibRaw_SOFInfo(): width(0),height(0),cps(0),precision(0),csfix(false){} |
245 | | bool parse_sof(ByteStreamBE&); // true => OK, false => not OK |
246 | | uint32_t parse_sos(ByteStreamBE&); // returns (predictor << 8 | point transform); if above 0xffff => error |
247 | | }; |
248 | | |
249 | | struct LibRaw_LjpegDecompressor |
250 | | { |
251 | | ByteStreamBE buffer; |
252 | | LibRaw_SOFInfo sof; |
253 | | uint32_t predictor, point_transform; |
254 | | uint32_t datastart; |
255 | | std::vector<HuffTable> dhts; |
256 | | LibRaw_LjpegDecompressor(uint8_t *b, unsigned s); |
257 | | LibRaw_LjpegDecompressor(uint8_t *b, unsigned bs, bool dngbug, bool csfix); |
258 | | void initialize(bool dngbug, bool csfix); |
259 | | uint8_t next_marker(bool allowskip); |
260 | | bool parse_dht(bool init[4], uint32_t dht_bits[4][17], uint32_t dht_huffval[4][256]); // return true on OK; |
261 | | bool decode_ljpeg_422(std::vector<uint16_t> &dest, int width, int height); |
262 | | |
263 | | struct State { |
264 | | enum States |
265 | | { |
266 | | OK = 0, |
267 | | NotInited = 1, |
268 | | NoSOI = 2, |
269 | | IncorrectPrecision = 3, |
270 | | EOIReached = 4, |
271 | | InvalidDHT = 5, |
272 | | InvalidSOF = 6, |
273 | | InvalidSOS = 7, |
274 | | DQTPresent = 8 |
275 | | }; |
276 | | |
277 | | }; |
278 | | struct Marker { |
279 | | enum Markers { |
280 | | Stuff = 0x00, |
281 | | SOF3 = 0xc3, // lossless |
282 | | DHT = 0xc4, // huffman tables |
283 | | SOI = 0xd8, // start of image |
284 | | EOI = 0xd9, // end of image |
285 | | SOS = 0xda, // start of scan |
286 | | DQT = 0xdb, // quantization tables |
287 | | Fill = 0xff, |
288 | | }; |
289 | | }; |
290 | | enum State::States state; |
291 | | }; |
292 | | |