Coverage Report

Created: 2026-02-14 07:11

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 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