Coverage Report

Created: 2026-10-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 uint32_t LIBRAW_DECODE_CACHE_MASK = (1<< LIBRAW_DECODE_CACHE_BITS)-1;
137
const uint64_t LIBRAW_CACHE_PRESENT_FLAG = 0x100000000L;
138
139
struct HuffTable
140
{
141
  uint32_t bits[17];
142
  uint32_t huffval[256];
143
  uint32_t shiftval[256];
144
  bool dng_bug;
145
  bool disable_cache;
146
  uint32_t nbits;
147
    std::vector<uint32_t> hufftable; // len:8 << 16| huffval:8  << 8 | shift:8
148
  std::vector<uint64_t> decodecache;
149
  bool initialized;
150
  HuffTable();
151
  void initval(uint32_t bits[17], uint32_t huffval[256], bool dng_bug);
152
153
  int32_t decode(BitPump& pump)
154
0
    {
155
0
      uint64_t cached = disable_cache ? 0 : decodecache[pump.peek(LIBRAW_DECODE_CACHE_BITS) & LIBRAW_DECODE_CACHE_MASK];
156
0
      if (cached & LIBRAW_CACHE_PRESENT_FLAG)
157
0
      {
158
0
        uint32_t _bits = (cached >> 16) & 0xff;
159
0
        int16_t val = int16_t(cached & 0xffff);
160
0
        if (val == -32768 && dng_bug)
161
0
        {
162
0
          if (_bits > 16)
163
0
            pump.consume(_bits - 16);
164
0
        }
165
0
        else
166
0
          pump.consume(_bits);
167
0
        return val;
168
0
      }
169
0
      else
170
0
        return decode_slow1(pump);
171
0
    }
172
173
  int32_t decode_slow1(BitPump &pump)
174
0
    {
175
0
      int32_t _diff = diff(pump, len(pump));
176
0
      return _diff;
177
0
    }
178
179
    int32_t decode_slow2(BitPump & pump, uint32_t& outlen) // output:  (len+shift):8, code:16
180
0
    {
181
0
      uint32_t _len = len(pump);
182
0
      int32_t _diff = diff(pump, _len);
183
0
      uint8_t bits8 = (_len >> 16) & 0xff;
184
0
      uint8_t len8 = (_len >> 8) & 0xff;
185
0
      outlen = bits8 + len8;
186
0
      return _diff;
187
0
    }
188
189
    uint32_t len(BitPump & pump) //bits:8, len:8, shift:8
190
0
    {
191
0
      uint32_t code = pump.peek(nbits);
192
0
      uint32_t huffdata = hufftable[code];
193
0
      uint32_t _bits = (huffdata >> 16) & 0xff;
194
0
      pump.consume(_bits);
195
0
      return huffdata;
196
0
    }
197
198
    int32_t diff(BitPump & pump, uint32_t hentry) // input: bits:8, len:8, shift:8; output:  diff:i32
199
0
    {
200
0
      uint32_t len = (hentry >> 8) & 0xff;
201
0
      if (len == 0)
202
0
        return 0;
203
0
      if (len == 16)
204
0
      {
205
0
        if (dng_bug)
206
0
          pump.get(16);
207
0
        return -32768;
208
0
      }
209
0
      uint32_t shift = hentry & 0xff;
210
0
    uint32_t fulllen = len + shift;
211
0
      uint32_t _bits = pump.get(len);
212
0
      int32_t diff = ((_bits << 1) + 1) << shift >> 1;
213
0
      if ((diff & (1 << (fulllen - 1))) == 0)
214
0
      {
215
0
        diff -= int32_t((1 << fulllen) - ((shift == 0)));
216
0
      }
217
0
      return diff;
218
0
    }
219
};
220
221
struct LibRaw_JpegComponentInfo
222
{
223
  unsigned id;
224
  unsigned index;
225
  unsigned dc_tbl;
226
  unsigned subsample_h, subsample_v;
227
0
  LibRaw_JpegComponentInfo() : id(0), index(0), dc_tbl(0), subsample_h(0), subsample_v(0) {};
228
  LibRaw_JpegComponentInfo(unsigned _id, unsigned _index, unsigned _dcn, unsigned _sh, unsigned _sv)
229
0
    : id(_id), index(_index), dc_tbl(_dcn), subsample_h(_sh), subsample_v(_sv){};
230
0
  LibRaw_JpegComponentInfo(const LibRaw_JpegComponentInfo& q): id(q.id), index(q.index), dc_tbl(q.dc_tbl),
231
0
      subsample_h(q.subsample_h),subsample_v(q.subsample_v){}
232
};
233
234
struct LibRaw_SOFInfo
235
{
236
  unsigned width, height;
237
  unsigned cps, precision;
238
  std::vector<LibRaw_JpegComponentInfo> components;
239
  bool csfix;
240
0
  LibRaw_SOFInfo(): width(0),height(0),cps(0),precision(0),csfix(false){}
241
  bool parse_sof(ByteStreamBE&); // true => OK, false => not OK
242
  uint32_t parse_sos(ByteStreamBE&); // returns (predictor << 8 | point transform); if above 0xffff => error
243
};
244
245
struct LibRaw_LjpegDecompressor 
246
{
247
  ByteStreamBE buffer;
248
  LibRaw_SOFInfo sof;
249
  uint32_t predictor, point_transform;
250
  uint32_t datastart;
251
  std::vector<HuffTable> dhts;
252
  LibRaw_LjpegDecompressor(uint8_t *b, unsigned s);
253
  LibRaw_LjpegDecompressor(uint8_t *b, unsigned bs, bool dngbug, bool csfix);
254
  void  initialize(bool dngbug, bool csfix);
255
    uint8_t next_marker(bool allowskip);
256
  bool  parse_dht(bool init[4], uint32_t dht_bits[4][17], uint32_t dht_huffval[4][256]); // return true on OK;
257
  bool decode_ljpeg_422(std::vector<uint16_t> &dest, int width, int height);
258
259
  struct State {
260
      enum States
261
      {
262
        OK = 0,
263
    NotInited = 1,
264
    NoSOI = 2,
265
    IncorrectPrecision = 3,
266
    EOIReached = 4,
267
    InvalidDHT = 5,
268
    InvalidSOF = 6,
269
        InvalidSOS = 7,
270
    DQTPresent = 8
271
      };
272
273
  };
274
  struct Marker {
275
    enum Markers {
276
      Stuff = 0x00,
277
      SOF3 = 0xc3, // lossless
278
      DHT = 0xc4,  // huffman tables
279
      SOI = 0xd8,  // start of image
280
      EOI = 0xd9,  // end of image
281
      SOS = 0xda,  // start of scan
282
      DQT = 0xdb,  // quantization tables
283
      Fill = 0xff,
284
    };
285
  };
286
  enum State::States state;
287
};
288