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