Coverage Report

Created: 2026-09-14 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/codecs/uncompressed/unc_decoder_legacybase.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2023 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 <cstring>
22
#include <algorithm>
23
#include <iostream>
24
#include <cassert>
25
#include <utility>
26
27
#if ((defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__PGI)) && __GNUC__ < 9) || (defined(__clang__) && __clang_major__ < 10)
28
#include <type_traits>
29
#else
30
#include <bit>
31
#endif
32
33
#include "common_utils.h"
34
#include "context.h"
35
#include "compression.h"
36
#include "error.h"
37
#include "libheif/heif.h"
38
#include "unc_types.h"
39
#include "unc_boxes.h"
40
#include "unc_codec.h"
41
#include "unc_decoder_legacybase.h"
42
#include "codecs/decoder.h"
43
44
45
unc_decoder_legacybase::unc_decoder_legacybase(uint32_t width, uint32_t height,
46
                                               const std::shared_ptr<const Box_cmpd>& cmpd,
47
                                               const std::shared_ptr<const Box_uncC>& uncC,
48
                                               const std::vector<uint32_t>& uncC_index_to_comp_ids)
49
198
    : unc_decoder(width, height, cmpd, uncC, uncC_index_to_comp_ids)
50
198
{
51
198
}
52
53
void unc_decoder_legacybase::ensureChannelList(std::shared_ptr<HeifPixelImage>& img)
54
198
{
55
198
  if (channelList.empty()) {
56
198
    buildChannelList(img);
57
198
  }
58
198
}
59
60
void unc_decoder_legacybase::buildChannelList(std::shared_ptr<HeifPixelImage>& img)
61
198
{
62
198
  uint32_t uncC_index = 0;
63
554
  for (Box_uncC::Component component : m_uncC->get_components()) {
64
554
    ChannelListEntry entry = buildChannelListEntry(uncC_index, component, img);
65
554
    channelList.push_back(entry);
66
554
    uncC_index++;
67
554
  }
68
198
}
69
70
void unc_decoder_legacybase::memcpy_to_native_endian(uint8_t* dst, uint32_t value, uint32_t bytes_per_sample)
71
126k
{
72
  // TODO: this assumes that the file endianness is always big-endian. The endianness flags in the uncC header are not taken into account yet.
73
74
126k
  if (bytes_per_sample==1) {
75
112k
    *dst = static_cast<uint8_t>(value);
76
112k
    return;
77
112k
  }
78
14.9k
  else if (std::endian::native == std::endian::big) {
79
0
    for (uint32_t i = 0; i < bytes_per_sample; i++) {
80
0
      dst[bytes_per_sample - 1 - i] = static_cast<uint8_t>((value >> (i * 8)) & 0xFF);
81
0
    }
82
0
  }
83
14.9k
  else {
84
44.7k
    for (uint32_t i = 0; i < bytes_per_sample; i++) {
85
29.8k
      dst[i] = static_cast<uint8_t>((value >> (i * 8)) & 0xFF);
86
29.8k
    }
87
14.9k
  }
88
126k
}
89
90
void unc_decoder_legacybase::processComponentSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_row_offset, uint32_t tile_column, uint32_t tile_x)
91
50.5k
{
92
50.5k
  uint64_t dst_col_number = static_cast<uint64_t>(tile_column) * entry.tile_width + tile_x;
93
50.5k
  uint64_t dst_column_offset = dst_col_number * entry.bytes_per_component_sample;
94
50.5k
  int val = srcBits.get_bits(entry.bits_per_component_sample); // get_bits() reads input in big-endian order
95
50.5k
  memcpy_to_native_endian(entry.dst_plane + dst_row_offset + dst_column_offset, val, entry.bytes_per_component_sample);
96
50.5k
}
97
98
// Handles the case where a row consists of a single component type
99
// Not valid for Pixel interleave
100
// Not valid for the Cb/Cr channels in Mixed Interleave
101
// Not valid for multi-Y pixel interleave
102
void unc_decoder_legacybase::processComponentRow(ChannelListEntry& entry, UncompressedBitReader& srcBits, uint64_t dst_row_offset, uint32_t tile_column)
103
1.51k
{
104
29.4k
  for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) {
105
27.9k
    if (entry.component_alignment != 0) {
106
3.77k
      srcBits.skip_to_byte_boundary();
107
3.77k
      int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample;
108
3.77k
      srcBits.skip_bits(numPadBits);
109
3.77k
    }
110
27.9k
    processComponentSample(srcBits, entry, dst_row_offset, tile_column, tile_x);
111
27.9k
  }
112
1.51k
  srcBits.skip_to_byte_boundary();
113
1.51k
}
114
115
void unc_decoder_legacybase::processComponentTileSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_offset, uint32_t tile_x)
116
72.8k
{
117
72.8k
  uint64_t dst_sample_offset = uint64_t{tile_x} * entry.bytes_per_component_sample;
118
72.8k
  int val = srcBits.get_bits(entry.bits_per_component_sample);
119
72.8k
  memcpy_to_native_endian(entry.dst_plane + dst_offset + dst_sample_offset, val, entry.bytes_per_component_sample);
120
72.8k
}
121
122
// Handles the case where a row consists of a single component type
123
// Not valid for Pixel interleave
124
// Not valid for the Cb/Cr channels in Mixed Interleave
125
// Not valid for multi-Y pixel interleave
126
void unc_decoder_legacybase::processComponentTileRow(ChannelListEntry& entry, UncompressedBitReader& srcBits, uint64_t dst_offset)
127
5.58k
{
128
78.4k
  for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) {
129
72.8k
    if (entry.component_alignment != 0) {
130
10.5k
      srcBits.skip_to_byte_boundary();
131
10.5k
      int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample;
132
10.5k
      srcBits.skip_bits(numPadBits);
133
10.5k
    }
134
72.8k
    processComponentTileSample(srcBits, entry, dst_offset, tile_x);
135
72.8k
  }
136
5.58k
  srcBits.skip_to_byte_boundary();
137
5.58k
}
138
139
140
unc_decoder_legacybase::ChannelListEntry unc_decoder_legacybase::buildChannelListEntry(uint32_t component_id,
141
                                                                                        Box_uncC::Component component,
142
                                                                                        std::shared_ptr<HeifPixelImage>& img)
143
554
{
144
554
  ChannelListEntry entry;
145
554
  entry.use_channel = map_uncompressed_component_to_channel(m_cmpd, component, &(entry.channel));
146
554
  entry.dst_plane = img->get_component(m_uncC_index_to_comp_ids[component_id], &(entry.dst_plane_stride));
147
554
  entry.tile_width = m_tile_width;
148
554
  entry.tile_height = m_tile_height;
149
554
  if ((entry.channel == heif_channel_Cb) || (entry.channel == heif_channel_Cr)) {
150
64
    if (m_uncC->get_sampling_type() == sampling_mode_422) {
151
10
      entry.tile_width /= 2;
152
10
    }
153
54
    else if (m_uncC->get_sampling_type() == sampling_mode_420) {
154
50
      entry.tile_width /= 2;
155
50
      entry.tile_height /= 2;
156
50
    }
157
158
    // chroma[0] is this entry's own plane; chroma[1] is the paired plane
159
    // (Cr if this entry is Cb, or vice versa). Each plane's width is read
160
    // from that plane itself -- Cb and Cr can be declared with different bit
161
    // depths, and reusing chroma[0]'s width for chroma[1] overruns it
162
    // (GHSA-x8r2-mggj-j6wr).
163
64
    heif_channel paired_channel = (entry.channel == heif_channel_Cb) ? heif_channel_Cr : heif_channel_Cb;
164
165
64
    entry.chroma_dst_plane[0] = entry.dst_plane;
166
64
    entry.chroma_dst_plane_stride[0] = entry.dst_plane_stride;
167
64
    entry.chroma_bytes_per_component_sample[0] = (component.component_bit_depth + 7) / 8;
168
169
64
    entry.chroma_dst_plane[1] = img->get_channel_memory(paired_channel, &(entry.chroma_dst_plane_stride[1]));
170
64
    entry.chroma_bytes_per_component_sample[1] = img->get_storage_bits_per_pixel(paired_channel) / 8;
171
64
  }
172
554
  entry.bits_per_component_sample = component.component_bit_depth;
173
554
  entry.component_alignment = component.component_align_size;
174
554
  entry.bytes_per_component_sample = (component.component_bit_depth + 7) / 8;
175
554
  entry.bytes_per_tile_row_src = entry.tile_width * entry.bytes_per_component_sample;
176
554
  return entry;
177
554
}
178
179
180