Coverage Report

Created: 2026-06-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/image/image_description.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2026 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
22
#include "image_description.h"
23
24
#include "codecs/uncompressed/unc_types.h"
25
26
#if WITH_UNCOMPRESSED_CODEC
27
#include "codecs/uncompressed/unc_boxes.h"
28
#endif
29
30
#include <algorithm>
31
32
33
heif_channel map_uncompressed_component_to_channel(uint16_t component_type)
34
3.36k
{
35
3.36k
  switch (component_type) {
36
46
    case heif_cmpd_component_type_monochrome:
37
283
    case heif_cmpd_component_type_Y:
38
283
      return heif_channel_Y;
39
234
    case heif_cmpd_component_type_Cb:
40
234
      return heif_channel_Cb;
41
235
    case heif_cmpd_component_type_Cr:
42
235
      return heif_channel_Cr;
43
832
    case heif_cmpd_component_type_red:
44
832
      return heif_channel_R;
45
824
    case heif_cmpd_component_type_green:
46
824
      return heif_channel_G;
47
823
    case heif_cmpd_component_type_blue:
48
823
      return heif_channel_B;
49
57
    case heif_cmpd_component_type_alpha:
50
57
      return heif_channel_Alpha;
51
9
    case heif_cmpd_component_type_filter_array:
52
9
      return heif_channel_filter_array;
53
0
    case heif_cmpd_component_type_depth:
54
0
      return heif_channel_depth;
55
0
    case heif_cmpd_component_type_disparity:
56
0
      return heif_channel_disparity;
57
58
63
    case heif_cmpd_component_type_padded:
59
72
    default:
60
72
      return heif_channel_unknown;
61
3.36k
  }
62
3.36k
}
63
64
65
BayerPatternCmpd BayerPattern::resolve_to_cmpd(std::map<uint32_t, uint32_t> comp_id_to_cmpd) const
66
0
{
67
0
  BayerPatternCmpd cpat;
68
0
  cpat.pattern_width = pattern_width;
69
0
  cpat.pattern_height = pattern_height;
70
71
0
  for (auto p : pixels) {
72
0
    assert(comp_id_to_cmpd.find(p.component_id) != comp_id_to_cmpd.end());
73
0
    cpat.pixels.push_back({comp_id_to_cmpd[p.component_id], p.component_gain});
74
0
  }
75
76
0
  return cpat;
77
0
}
78
79
80
static void remove_duplicates(std::vector<uint32_t>& v)
81
0
{
82
0
  std::sort(v.begin(), v.end());
83
0
  v.erase(std::unique(v.begin(), v.end()), v.end());
84
0
}
85
86
87
std::vector<uint32_t> map_component_ids_to_cmpd(const std::vector<uint32_t>& component_ids, const std::map<uint32_t, uint32_t>& comp_id_to_cmpd)
88
0
{
89
0
  std::vector<uint32_t> cmpd_indices;
90
91
0
  for (uint32_t comp_id : component_ids) {
92
0
    assert(comp_id_to_cmpd.find(comp_id) != comp_id_to_cmpd.end());
93
0
    cmpd_indices.push_back(comp_id_to_cmpd.find(comp_id)->second);
94
0
  }
95
96
0
  remove_duplicates(cmpd_indices);
97
98
0
  return cmpd_indices;
99
0
}
100
101
std::vector<uint32_t> map_cmpd_to_component_ids(const std::vector<uint32_t>& cmpd_indices, const std::vector<std::vector<uint32_t>>& cmpd_to_comp_ids)
102
0
{
103
0
  std::vector<uint32_t> component_ids;
104
105
0
  for (uint32_t idx : cmpd_indices) {
106
0
    assert(idx < cmpd_to_comp_ids.size());
107
0
    component_ids.insert(component_ids.end(), cmpd_to_comp_ids[idx].begin(), cmpd_to_comp_ids[idx].end());
108
0
  }
109
110
0
  remove_duplicates(component_ids);
111
112
0
  return component_ids;
113
0
}
114
115
116
ImageDescription::~ImageDescription()
117
168k
{
118
168k
  heif_tai_timestamp_packet_release(m_tai_timestamp);
119
168k
}
120
121
122
void ImageDescription::copy_metadata_from(const ImageDescription& other)
123
22.9k
{
124
22.9k
  m_premultiplied_alpha = other.m_premultiplied_alpha;
125
22.9k
  m_color_profile_nclx = other.m_color_profile_nclx;
126
22.9k
  m_color_profile_icc = other.m_color_profile_icc;
127
128
22.9k
  m_PixelAspectRatio_h = other.m_PixelAspectRatio_h;
129
22.9k
  m_PixelAspectRatio_v = other.m_PixelAspectRatio_v;
130
131
22.9k
  m_clli = other.m_clli;
132
22.9k
  m_mdcv = other.m_mdcv;
133
22.9k
  m_amve = other.m_amve;
134
22.9k
  m_nominal_diffuse_white_luminance = other.m_nominal_diffuse_white_luminance;
135
136
22.9k
  heif_tai_timestamp_packet_release(m_tai_timestamp);
137
22.9k
  m_tai_timestamp = nullptr;
138
22.9k
  if (other.m_tai_timestamp) {
139
0
    m_tai_timestamp = heif_tai_timestamp_packet_alloc();
140
0
    heif_tai_timestamp_packet_copy(m_tai_timestamp, other.m_tai_timestamp);
141
0
  }
142
143
22.9k
  m_gimi_sample_content_id = other.m_gimi_sample_content_id;
144
145
22.9k
  m_bayer_pattern = other.m_bayer_pattern;
146
22.9k
  m_polarization_patterns = other.m_polarization_patterns;
147
22.9k
  m_sensor_bad_pixels_maps = other.m_sensor_bad_pixels_maps;
148
22.9k
  m_sensor_nuc = other.m_sensor_nuc;
149
150
22.9k
  m_chroma_location = other.m_chroma_location;
151
152
22.9k
  m_sample_duration = other.m_sample_duration;
153
154
22.9k
  m_omaf_image_projection = other.m_omaf_image_projection;
155
22.9k
}
156
157
158
bool ImageDescription::has_nclx_color_profile() const
159
132k
{
160
132k
  return m_color_profile_nclx != nclx_profile::undefined();
161
132k
}
162
163
164
nclx_profile ImageDescription::get_color_profile_nclx_with_fallback() const
165
20.5k
{
166
20.5k
  if (has_nclx_color_profile()) {
167
15.3k
    return get_color_profile_nclx();
168
15.3k
  }
169
5.23k
  else {
170
5.23k
    return nclx_profile::defaults();
171
5.23k
  }
172
20.5k
}
173
174
175
std::shared_ptr<Box_clli> ImageDescription::create_clli_box() const
176
0
{
177
0
  if (!has_clli()) {
178
0
    return {};
179
0
  }
180
181
0
  auto clli = std::make_shared<Box_clli>();
182
0
  clli->clli = get_clli();
183
184
0
  return clli;
185
0
}
186
187
188
std::shared_ptr<Box_mdcv> ImageDescription::create_mdcv_box() const
189
0
{
190
0
  if (!has_mdcv()) {
191
0
    return {};
192
0
  }
193
194
0
  auto mdcv = std::make_shared<Box_mdcv>();
195
0
  mdcv->mdcv = get_mdcv();
196
197
0
  return mdcv;
198
0
}
199
200
201
std::shared_ptr<Box_amve> ImageDescription::create_amve_box() const
202
0
{
203
0
  if (!has_amve()) {
204
0
    return {};
205
0
  }
206
207
0
  auto amve = std::make_shared<Box_amve>();
208
0
  amve->amve = get_amve();
209
210
0
  return amve;
211
0
}
212
213
214
std::shared_ptr<Box_ndwt> ImageDescription::create_ndwt_box() const
215
0
{
216
0
  if (!has_nominal_diffuse_white()) {
217
0
    return {};
218
0
  }
219
220
0
  auto ndwt = std::make_shared<Box_ndwt>();
221
0
  ndwt->set_diffuse_white_luminance(get_nominal_diffuse_white_luminance());
222
223
0
  return ndwt;
224
0
}
225
226
227
std::shared_ptr<Box_pasp> ImageDescription::create_pasp_box() const
228
0
{
229
0
  if (!has_nonsquare_pixel_ratio()) {
230
0
    return {};
231
0
  }
232
233
0
  auto pasp = std::make_shared<Box_pasp>();
234
0
  pasp->hSpacing = m_PixelAspectRatio_h;
235
0
  pasp->vSpacing = m_PixelAspectRatio_v;
236
237
0
  return pasp;
238
0
}
239
240
241
std::shared_ptr<Box_colr> ImageDescription::create_colr_box_nclx() const
242
17.3k
{
243
17.3k
  if (!has_nclx_color_profile()) {
244
323
    return {};
245
323
  }
246
247
17.0k
  auto colr = std::make_shared<Box_colr>();
248
17.0k
  colr->set_color_profile(std::make_shared<color_profile_nclx>(get_color_profile_nclx()));
249
17.0k
  return colr;
250
17.3k
}
251
252
253
std::shared_ptr<Box_colr> ImageDescription::create_colr_box_icc() const
254
15.7k
{
255
15.7k
  if (!has_icc_color_profile()) {
256
0
    return {};
257
0
  }
258
259
15.7k
  auto colr = std::make_shared<Box_colr>();
260
15.7k
  colr->set_color_profile(get_color_profile_icc());
261
15.7k
  return colr;
262
15.7k
}
263
264
std::shared_ptr<Box_prfr> ImageDescription::create_prfr_box() const
265
0
{
266
0
  if (!has_omaf_image_projection()) {
267
0
    return {};
268
0
  }
269
270
0
  auto prfr = std::make_shared<Box_prfr>();
271
0
  if (prfr->set_image_projection(get_omaf_image_projection())) {
272
0
    return {};
273
0
  }
274
275
0
  return prfr;
276
0
}
277
278
std::vector<std::shared_ptr<Box>> ImageDescription::generate_property_boxes(bool generate_colr_boxes) const
279
0
{
280
0
  std::vector<std::shared_ptr<Box>> properties;
281
282
  // --- write PASP property
283
284
0
  if (has_nonsquare_pixel_ratio()) {
285
0
    auto pasp = std::make_shared<Box_pasp>();
286
0
    get_pixel_ratio(&pasp->hSpacing, &pasp->vSpacing);
287
288
0
    properties.push_back(pasp);
289
0
  }
290
291
292
  // --- write CLLI property
293
294
0
  if (has_clli()) {
295
0
    properties.push_back(create_clli_box());
296
0
  }
297
298
299
  // --- write MDCV property
300
301
0
  if (has_mdcv()) {
302
0
    auto mdcv = std::make_shared<Box_mdcv>();
303
0
    mdcv->mdcv = get_mdcv();
304
305
0
    properties.push_back(mdcv);
306
0
  }
307
308
309
  // --- write AMVE property
310
311
0
  if (has_amve()) {
312
0
    properties.push_back(create_amve_box());
313
0
  }
314
315
316
  // --- write NDWT property
317
318
0
  if (has_nominal_diffuse_white()) {
319
0
    properties.push_back(create_ndwt_box());
320
0
  }
321
322
323
  // --- write TAI property
324
325
0
  if (auto* tai = get_tai_timestamp()) {
326
0
    auto itai = std::make_shared<Box_itai>();
327
0
    itai->set_from_tai_timestamp_packet(tai);
328
329
0
    properties.push_back(itai);
330
0
  }
331
332
  // --- write GIMI content ID property
333
334
0
  if (has_gimi_sample_content_id()) {
335
0
    auto gimi = std::make_shared<Box_gimi_content_id>();
336
0
    gimi->set_content_id(get_gimi_sample_content_id());
337
0
    properties.push_back(gimi);
338
0
  }
339
340
0
  if (generate_colr_boxes) {
341
    // --- colr (nclx)
342
343
0
    if (has_nclx_color_profile()) {
344
0
      properties.push_back(create_colr_box_nclx());
345
0
    }
346
347
    // --- colr (icc)
348
349
0
    if (has_icc_color_profile()) {
350
0
      properties.push_back(create_colr_box_icc());
351
0
    }
352
0
  }
353
354
0
  if (has_omaf_image_projection()) {
355
0
    auto prfr = std::make_shared<Box_prfr>();
356
0
    prfr->set_image_projection(get_omaf_image_projection());
357
0
    properties.push_back(prfr);
358
0
  }
359
360
0
#if WITH_UNCOMPRESSED_CODEC
361
0
  if (has_component_content_ids()) {
362
0
    auto ccid = std::make_shared<Box_gimi_component_content_ids>();
363
0
    ccid->set_content_ids(get_component_content_ids());
364
0
    properties.push_back(ccid);
365
0
  }
366
0
#endif
367
368
0
  return properties;
369
0
}