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