Coverage Report

Created: 2026-09-13 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/codecs/jpeg_dec.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "jpeg_dec.h"
22
#include "jpeg_boxes.h"
23
#include "error.h"
24
#include "context.h"
25
26
#include <string>
27
#include <algorithm>
28
29
30
Result<std::vector<uint8_t>> Decoder_JPEG::read_bitstream_configuration_data() const
31
541
{
32
541
  if (m_jpgC) {
33
0
    return m_jpgC->get_data();
34
0
  }
35
541
  else {
36
541
    return std::vector<uint8_t>{};
37
541
  }
38
541
}
39
40
41
// This checks whether a start code FFCx with nibble 'x' is a SOF marker.
42
// E.g. FFC0-FFC3 are, while FFC4 is not.
43
static bool isSOF[16] = {true, true, true, true, false, true, true, true,
44
                         false, true, true, true, false, true, true, true};
45
46
Error Decoder_JPEG::parse_SOF()
47
567
{
48
567
  if (m_config) {
49
26
    return Error::Ok;
50
26
  }
51
52
  // image data, usually from 'mdat'
53
54
541
  auto dataResult = get_compressed_data(true);
55
541
  if (!dataResult) {
56
188
    return dataResult.error();
57
188
  }
58
59
353
  const std::vector<uint8_t>& data = *dataResult;
60
61
353
  Error error_invalidSOF{heif_error_Invalid_input,
62
353
                               heif_suberror_Unspecified,
63
353
                               "Invalid JPEG SOF header"};
64
65
49.5k
  for (size_t i = 0; i + 1 < data.size(); i++) {
66
49.2k
    if (data[i] == 0xFF && (data[i + 1] & 0xF0) == 0xC0 && isSOF[data[i + 1] & 0x0F]) {
67
68
127
      if (i + 9 >= data.size()) {
69
18
        return error_invalidSOF;
70
18
      }
71
72
109
      ConfigInfo info;
73
109
      info.sample_precision = data[i + 4];
74
109
      info.nComponents = data[i + 9];
75
76
109
      if (i + 11 + 3 * info.nComponents >= data.size()) {
77
25
        return error_invalidSOF;
78
25
      }
79
80
292
      for (int c = 0; c < std::min(info.nComponents, uint8_t(3)); c++) {
81
208
        int ss = data[i + 11 + 3 * c];
82
208
        info.h_sampling[c] = (ss >> 4) & 0xF;
83
208
        info.v_sampling[c] = ss & 0xF;
84
208
      }
85
86
84
      if (info.nComponents == 1) {
87
3
        info.chroma = heif_chroma_monochrome;
88
3
      }
89
81
      else if (info.nComponents != 3) {
90
28
        return error_invalidSOF;
91
28
      }
92
53
      else {
93
53
        if (info.h_sampling[1] != info.h_sampling[2] ||
94
49
            info.v_sampling[1] != info.v_sampling[2]) {
95
10
          return error_invalidSOF;
96
10
        }
97
98
43
        if (info.h_sampling[0] == 2 && info.v_sampling[0] == 2 &&
99
9
            info.h_sampling[1] == 1 && info.v_sampling[1] == 1) {
100
1
          info.chroma = heif_chroma_420;
101
1
        }
102
42
        else if (info.h_sampling[0] == 2 && info.v_sampling[0] == 1 &&
103
12
                 info.h_sampling[1] == 1 && info.v_sampling[1] == 1) {
104
2
          info.chroma = heif_chroma_422;
105
2
        }
106
40
        else if (info.h_sampling[0] == 1 && info.v_sampling[0] == 1 &&
107
8
                 info.h_sampling[1] == 1 && info.v_sampling[1] == 1) {
108
1
          info.chroma = heif_chroma_444;
109
1
        }
110
39
        else {
111
39
          return error_invalidSOF;
112
39
        }
113
43
      }
114
115
7
      m_config = info;
116
117
7
      return Error::Ok;
118
84
    }
119
49.2k
  }
120
121
226
  return error_invalidSOF;
122
353
}
123
124
125
int Decoder_JPEG::get_luma_bits_per_pixel() const
126
163
{
127
163
  Error err = const_cast<Decoder_JPEG*>(this)->parse_SOF();
128
163
  if (err) {
129
142
    return -1;
130
142
  }
131
21
  else {
132
21
    return m_config->sample_precision;
133
21
  }
134
163
}
135
136
137
int Decoder_JPEG::get_chroma_bits_per_pixel() const
138
9
{
139
9
  return get_luma_bits_per_pixel();
140
9
}
141
142
143
Error Decoder_JPEG::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const
144
404
{
145
404
  Error err = const_cast<Decoder_JPEG*>(this)->parse_SOF();
146
404
  if (err) {
147
392
    return err;
148
392
  }
149
150
12
  *out_chroma = m_config->chroma;
151
152
12
  if (*out_chroma == heif_chroma_monochrome) {
153
5
    *out_colorspace = heif_colorspace_monochrome;
154
5
  }
155
7
  else {
156
7
    *out_colorspace = heif_colorspace_YCbCr;
157
7
  }
158
159
12
  return Error::Ok;
160
404
}