Coverage Report

Created: 2026-09-06 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/image/pixelimage.h
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 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
#ifndef LIBHEIF_IMAGE_H
23
#define LIBHEIF_IMAGE_H
24
25
#include "image_description.h"
26
#include "security_limits.h"
27
28
#include <vector>
29
#include <memory>
30
#include <set>
31
#include <utility>
32
#include <cassert>
33
34
constexpr uint16_t heif_cmpd_component_type_UNDEFINED = 0x7FFF;
35
36
37
heif_chroma chroma_from_subsampling(int h, int v);
38
39
uint32_t chroma_width(uint32_t w, heif_chroma chroma);
40
41
uint32_t chroma_height(uint32_t h, heif_chroma chroma);
42
43
uint32_t channel_width(uint32_t w, heif_chroma chroma, heif_channel channel);
44
45
uint32_t channel_height(uint32_t h, heif_chroma chroma, heif_channel channel);
46
47
bool is_interleaved_with_alpha(heif_chroma chroma);
48
49
int num_interleaved_components_per_plane(heif_chroma chroma);
50
51
bool is_integer_multiple_of_chroma_size(uint32_t width,
52
                                        uint32_t height,
53
                                        heif_chroma chroma);
54
55
// Returns the list of valid heif_chroma values for a given colorspace.
56
std::vector<heif_chroma> get_valid_chroma_values_for_colorspace(heif_colorspace colorspace);
57
58
59
60
class HeifPixelImage : public std::enable_shared_from_this<HeifPixelImage>,
61
                       public ImageDescription,
62
                       public ErrorBuffer
63
{
64
public:
65
61.7k
  explicit HeifPixelImage() = default;
66
67
  ~HeifPixelImage() override;
68
69
  void create(uint32_t width, uint32_t height, heif_colorspace colorspace, heif_chroma chroma);
70
71
  Error create_clone_image_at_new_size(const std::shared_ptr<const HeifPixelImage>& source, uint32_t w, uint32_t h,
72
                                       const heif_security_limits* limits);
73
74
  Error add_channel(heif_channel channel, uint32_t width, uint32_t height, int bit_depth,
75
                  const heif_security_limits* limits,
76
                  heif_component_datatype datatype = heif_component_datatype_unsigned_integer);
77
78
  bool has_channel(heif_channel channel) const;
79
80
  // Has alpha information either as a separate channel or in the interleaved format.
81
  bool has_alpha() const;
82
83
  // Get logical image size. Individual components may vary.
84
64.5k
  uint32_t get_width() const { return m_width; }
85
86
  // Get logical image size. Individual components may vary.
87
63.8k
  uint32_t get_height() const { return m_height; }
88
89
  uint32_t get_width(heif_channel channel) const;
90
91
  uint32_t get_height(heif_channel channel) const;
92
93
  uint32_t get_width(uint32_t component_id) const;
94
95
  uint32_t get_height(uint32_t component_id) const;
96
97
269
  bool has_odd_width() const { return !!(m_width & 1); }
98
99
250
  bool has_odd_height() const { return !!(m_height & 1); }
100
101
  // Returns true if the "primary" pixel plane(s) have exactly the given size.
102
  // Which plane is "primary" depends on the colorspace:
103
  //   YCbCr / monochrome  -> the Y plane (Cb/Cr may be legitimately subsampled)
104
  //   RGB planar          -> R, G and B planes must all be present and all equal
105
  //   RGB interleaved     -> the interleaved plane
106
  //   filter_array        -> the filter_array plane
107
  //   undefined / custom  -> not checked here; returns true
108
  bool primary_planes_have_size(uint32_t width, uint32_t height) const;
109
110
  // Returns true if every plane actually stored in m_storage -- not just the
111
  // "primary" ones for the current colorspace -- has the size that plane's
112
  // channel is expected to have: m_width x m_height for Y/Alpha/R/G/B/
113
  // interleaved, and the chroma-subsampled size (via get_subsampled_size())
114
  // for Cb/Cr. Any other channel type fails the check. Unlike
115
  // primary_planes_have_size(), this checks each stored component directly
116
  // rather than going through get_width(channel)/get_height(channel) (which
117
  // only ever sees the first plane for a channel), so it also catches a
118
  // same-channel duplicate whose size doesn't match, not just a missing or
119
  // undersized single plane.
120
  bool has_standard_plane_sizes() const;
121
122
108k
  heif_chroma get_chroma_format() const { return m_chroma; }
123
124
98.5k
  heif_colorspace get_colorspace() const { return m_colorspace; }
125
126
  std::set<heif_channel> get_channel_set() const;
127
128
  uint16_t get_storage_bits_per_pixel(heif_channel channel) const;
129
130
  uint16_t get_bits_per_pixel(heif_channel channel) const;
131
132
  // Get the maximum bit depth of a visual channel (YCbCr or RGB).
133
  uint16_t get_visual_image_bits_per_pixel() const;
134
135
  heif_component_datatype get_datatype(heif_channel channel) const;
136
137
  int get_number_of_interleaved_components(heif_channel channel) const;
138
139
  // Note: we are using size_t as stride type since the stride is usually involved in a multiplication with the line number.
140
  //       For very large images (e.g. >2 GB), this can result in an integer overflow and corresponding illegal memory access.
141
  //       (see https://github.com/strukturag/libheif/issues/1419)
142
215k
  uint8_t* get_channel_memory(heif_channel channel, size_t* out_stride) { return get_channel_memory<uint8_t>(channel, out_stride); }
143
144
63.6k
  const uint8_t* get_channel_memory(heif_channel channel, size_t* out_stride) const { return get_channel_memory<uint8_t>(channel, out_stride); }
145
146
  template <typename T>
147
  T* get_channel_memory(heif_channel channel, size_t* out_stride)
148
279k
  {
149
279k
    auto* comp = find_storage_for_channel(channel);
150
279k
    if (!comp) {
151
0
      if (out_stride)
152
0
        *out_stride = 0;
153
154
0
      return nullptr;
155
0
    }
156
157
279k
    if (out_stride) {
158
279k
      *out_stride = static_cast<int>(comp->stride);
159
279k
    }
160
161
279k
    return static_cast<T*>(comp->mem);
162
279k
  }
unsigned char* HeifPixelImage::get_channel_memory<unsigned char>(heif_channel, unsigned long*)
Line
Count
Source
148
279k
  {
149
279k
    auto* comp = find_storage_for_channel(channel);
150
279k
    if (!comp) {
151
0
      if (out_stride)
152
0
        *out_stride = 0;
153
154
0
      return nullptr;
155
0
    }
156
157
279k
    if (out_stride) {
158
279k
      *out_stride = static_cast<int>(comp->stride);
159
279k
    }
160
161
279k
    return static_cast<T*>(comp->mem);
162
279k
  }
unsigned short* HeifPixelImage::get_channel_memory<unsigned short>(heif_channel, unsigned long*)
Line
Count
Source
148
26
  {
149
26
    auto* comp = find_storage_for_channel(channel);
150
26
    if (!comp) {
151
0
      if (out_stride)
152
0
        *out_stride = 0;
153
154
0
      return nullptr;
155
0
    }
156
157
26
    if (out_stride) {
158
26
      *out_stride = static_cast<int>(comp->stride);
159
26
    }
160
161
26
    return static_cast<T*>(comp->mem);
162
26
  }
163
164
  template <typename T>
165
  const T* get_channel_memory(heif_channel channel, size_t* out_stride) const
166
63.6k
  {
167
63.6k
    return const_cast<HeifPixelImage*>(this)->get_channel_memory<T>(channel, out_stride);
168
63.6k
  }
169
170
171
  // --- id-based component access (for ISO 23001-17 multi-component images)
172
173
0
  uint32_t get_number_of_used_components() const { return static_cast<uint32_t>(get_component_descriptions().size()); }
174
175
  //uint32_t get_total_number_of_cmpd_components() const { return static_cast<uint32_t>(m_cmpd_component_types.size()); }
176
177
  heif_channel get_component_channel(uint32_t component_id) const;
178
179
  uint32_t get_component_width(uint32_t component_id) const;
180
  uint32_t get_component_height(uint32_t component_id) const;
181
  uint16_t get_component_bits_per_pixel(uint32_t component_id) const;
182
  uint16_t get_component_storage_bits_per_pixel(uint32_t component_id) const;
183
  heif_component_datatype get_component_datatype(uint32_t component_id) const;
184
185
  // Look up the component type from the cmpd table. Works for any cmpd index,
186
  // even those that have no image plane (e.g. bayer reference components).
187
  uint16_t get_component_type(uint32_t component_id) const;
188
189
  //std::vector<uint16_t> get_cmpd_component_types() const { return m_cmpd_component_types; }
190
191
  std::vector<uint32_t> get_component_ids_interleaved() const;
192
193
  //uint32_t get_component_cmpd_index() const { assert(m_cmpd_component_types.size()==1); return m_cmpd_component_types[0]; }
194
195
  // Encoder path: auto-generates component_index by appending to cmpd table.
196
  Result<uint32_t> add_component(uint32_t width, uint32_t height,
197
                                 uint16_t component_type,
198
                                 heif_component_datatype datatype, int bit_depth,
199
                                 const heif_security_limits* limits);
200
201
  // TODO: replace uint16_t component_type with class that also handled the std::string type
202
  uint32_t add_component_without_data(uint16_t component_type);
203
204
  // Decoder path: copy the per-component description from a source
205
  // ImageDescription (typically the ImageItem the decoder is about to populate).
206
  // After this, allocate_buffer_for_component() can be called with each id
207
  // already present in m_components to allocate its pixel buffer.
208
  void clone_component_descriptions_from(const ImageDescription& src);
209
210
  // Post-decode reconciliation: rebind this image's component descriptions
211
  // and the m_component_ids on each plane so they match `src` (typically the
212
  // ImageItem). Used after a plugin-based codec decode returns: the plugin
213
  // auto-minted ids via the public C API (heif_image_add_plane_safe), but
214
  // the handle has a canonical description list we want to expose.
215
  // Channels in `src` overwrite this image's matching descriptions.
216
  // Channels present in this image but not in `src` (e.g. alpha-from-aux
217
  // attached after main decode) are kept under fresh ids that won't collide
218
  // with `src`. No-op if `src` has no descriptions.
219
  void apply_descriptions_from(const ImageDescription& src);
220
221
  // Decoder path: allocate a pixel buffer for a component whose description
222
  // (channel, datatype, bit_depth, width, height) is already in m_components.
223
  // No new id is minted. Used after clone_component_descriptions_from().
224
  Error allocate_buffer_for_component(uint32_t component_id,
225
                                      const heif_security_limits* limits);
226
227
  // Decoder path: uses a pre-populated cmpd table to look up the component type.
228
#if 0
229
  Result<uint32_t> add_component_for_index(uint32_t component_index,
230
                                            uint32_t width, uint32_t height,
231
                                            heif_component_datatype datatype, int bit_depth,
232
                                            const heif_security_limits* limits);
233
#endif
234
235
  // Populate the cmpd component types table (decoder path).
236
  //void set_cmpd_component_types(std::vector<uint16_t> types) { m_cmpd_component_types = std::move(types); }
237
238
  //const std::vector<uint16_t>& get_cmpd_component_types() { return m_cmpd_component_types; }
239
240
  // Returns the component ids of all components, in component-description
241
  // order. This includes reference components that have no pixel plane
242
  // (has_data_plane == false); the result size always equals
243
  // get_number_of_used_components().
244
  std::vector<uint32_t> get_used_component_ids() const;
245
246
  std::vector<uint32_t> get_used_planar_component_ids() const;
247
248
  uint8_t* get_component(uint32_t component_id, size_t* out_stride);
249
  const uint8_t* get_component(uint32_t component_id, size_t* out_stride) const;
250
251
  template <typename T>
252
  T* get_component_memory(uint32_t component_id, size_t* out_stride)
253
2.51k
  {
254
2.51k
    auto* comp = find_storage_for_component(component_id);
255
2.51k
    if (!comp) {
256
0
      if (out_stride) *out_stride = 0;
257
0
      return nullptr;
258
0
    }
259
260
2.51k
    if (out_stride) {
261
2.51k
      *out_stride = comp->stride;
262
2.51k
    }
263
2.51k
    return static_cast<T*>(comp->mem);
264
2.51k
  }
265
266
  template <typename T>
267
  const T* get_component_memory(uint32_t component_id, size_t* out_stride) const
268
0
  {
269
0
    return const_cast<HeifPixelImage*>(this)->get_component_memory<T>(component_id, out_stride);
270
0
  }
271
272
  Error copy_new_channel_from(const std::shared_ptr<const HeifPixelImage>& src_image,
273
                            heif_channel src_channel,
274
                            heif_channel dst_channel,
275
                            const heif_security_limits* limits);
276
277
  Error extract_alpha_from_RGBA(const std::shared_ptr<const HeifPixelImage>& srcimage, const heif_security_limits* limits);
278
279
  void fill_channel(heif_channel dst_channel, uint16_t value);
280
281
  Error fill_new_channel(heif_channel dst_channel, uint16_t value, int width, int height, int bpp, const heif_security_limits* limits);
282
283
  Error transfer_channel_from_image_as(const std::shared_ptr<HeifPixelImage>& source,
284
                                    heif_channel src_channel,
285
                                    heif_channel dst_channel);
286
287
  Error copy_image_to(const std::shared_ptr<const HeifPixelImage>& source, uint32_t x0, uint32_t y0);
288
289
  Result<std::shared_ptr<HeifPixelImage>> rotate_ccw(int angle_degrees, const heif_security_limits* limits);
290
291
  Result<std::shared_ptr<HeifPixelImage>> mirror_inplace(heif_transform_mirror_direction, const heif_security_limits* limits);
292
293
  Result<std::shared_ptr<HeifPixelImage>> crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom,
294
                                               const heif_security_limits* limits) const;
295
296
  Error fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_t a);
297
298
  Error overlay(std::shared_ptr<HeifPixelImage>& overlay, int32_t dx, int32_t dy);
299
300
  Error scale_nearest_neighbor(std::shared_ptr<HeifPixelImage>& output, uint32_t width, uint32_t height,
301
                               const heif_security_limits* limits) const;
302
303
304
  void debug_dump() const;
305
306
  Error extend_padding_to_size(uint32_t width, uint32_t height, bool adjust_size,
307
                               const heif_security_limits* limits);
308
309
  Error extend_to_size_with_zero(uint32_t width, uint32_t height, const heif_security_limits* limits);
310
311
  Result<std::shared_ptr<HeifPixelImage>> extract_image_area(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h,
312
                                                             const heif_security_limits* limits) const;
313
314
315
  // --- warnings
316
317
16.6k
  void add_warning(Error warning) { m_warnings.emplace_back(std::move(warning)); }
318
319
21.3k
  void add_warnings(const std::vector<Error>& warning) { for (const auto& err : warning) m_warnings.emplace_back(err); }
320
321
21.9k
  const std::vector<Error>& get_warnings() const { return m_warnings; }
322
323
private:
324
  struct ComponentStorage
325
  {
326
    heif_channel m_channel = heif_channel_Y;
327
328
    // index into the cmpd component definition table
329
    // Interleaved channels will have a list of indices in the order R,G,B,A
330
    std::vector<uint32_t> m_component_ids;
331
332
    // limits=nullptr disables the limits
333
    Error alloc(uint32_t width, uint32_t height, heif_component_datatype datatype, int bit_depth,
334
                int num_interleaved_components,
335
                const heif_security_limits* limits,
336
                MemoryHandle& memory_handle);
337
338
    heif_component_datatype m_datatype = heif_component_datatype_unsigned_integer;
339
340
    // logical bit depth per component
341
    // For interleaved formats, it is the number of bits for one component.
342
    // It is not the storage width.
343
    uint16_t m_bit_depth = 0; // 1-256
344
    uint8_t m_num_interleaved_components = 1;
345
346
    // Cached at alloc() time so get_bytes_per_pixel() doesn't have to do
347
    // the bit-depth → bytes ladder on every call. Equal to
348
    // bytes_per_component * m_num_interleaved_components.
349
    uint8_t m_bytes_per_pixel = 0;
350
351
    // the "visible" area of the component
352
    uint32_t m_width = 0;
353
    uint32_t m_height = 0;
354
355
    // the allocated memory size
356
    uint32_t m_mem_width = 0;
357
    uint32_t m_mem_height = 0;
358
359
    void* mem = nullptr; // aligned memory start
360
    uint8_t* allocated_mem = nullptr; // unaligned memory we allocated
361
    size_t   allocation_size = 0;
362
    size_t   stride = 0; // bytes per line
363
364
    int get_bytes_per_pixel() const;
365
366
    template <typename T> void mirror_inplace(heif_transform_mirror_direction);
367
368
    template<typename T>
369
    void rotate_ccw(int angle_degrees, ComponentStorage& out_plane) const;
370
371
    void crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom, int bytes_per_pixel, ComponentStorage& out_plane) const;
372
  };
373
374
  ComponentStorage* find_storage_for_channel(heif_channel channel);
375
  const ComponentStorage* find_storage_for_channel(heif_channel channel) const;
376
377
  ComponentStorage* find_storage_for_component(uint32_t component_id);
378
  const ComponentStorage* find_storage_for_component(uint32_t component_id) const;
379
380
  // After plane.alloc() has succeeded, mints fresh component ids, appends
381
  // them to plane.m_component_ids, and pushes fully-populated
382
  // ComponentDescription entries for each component_type. Channel, datatype,
383
  // bit_depth, width and height are read from `plane`. Must only be called
384
  // once allocation has succeeded so that no descriptions are registered for
385
  // a plane that failed to materialize.
386
  void register_component_descriptions(ComponentStorage& plane,
387
                                       const std::vector<uint16_t>& component_types);
388
389
  // Overload that clones existing ComponentDescriptions (preserving
390
  // component_type, gimi_content_id, has_data_plane, and any other per-id
391
  // metadata). Geometry/datatype/bit_depth/channel fields are overwritten
392
  // from `plane`; component_ids are freshly minted on this image. Use this
393
  // when allocating a new plane that mirrors an existing one (e.g.
394
  // geometry-preserving transforms like rotation and crop).
395
  void register_component_descriptions(ComponentStorage& plane,
396
                                       const std::vector<const ComponentDescription*>& source_descriptions);
397
398
  uint32_t m_width = 0;
399
  uint32_t m_height = 0;
400
  heif_colorspace m_colorspace = heif_colorspace_undefined;
401
  heif_chroma m_chroma = heif_chroma_undefined;
402
403
  std::vector<ComponentStorage> m_storage;
404
  MemoryHandle m_memory_handle;
405
406
  std::vector<Error> m_warnings;
407
};
408
409
#endif