Coverage Report

Created: 2026-10-28 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/common_utils.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 "common_utils.h"
22
#include <cassert>
23
24
25
uint8_t chroma_h_subsampling(heif_chroma c)
26
107k
{
27
107k
  switch (c) {
28
0
    case heif_chroma_monochrome:
29
36.7k
    case heif_chroma_444:
30
36.7k
      return 1;
31
32
64.1k
    case heif_chroma_420:
33
70.9k
    case heif_chroma_422:
34
70.9k
      return 2;
35
36
0
    case heif_chroma_interleaved_RGB:
37
0
    case heif_chroma_interleaved_RGBA:
38
0
    default:
39
      // Interleaved and undefined chroma have no Cb/Cr subsampling of their own
40
      // (a Cb/Cr channel paired with one of these is already an inconsistent
41
      // image -- Cb/Cr only exist for planar YCbCr -- which other checks reject
42
      // on their own merits). Callers divide by this return value, and this
43
      // function is reachable with attacker-controlled chroma through the
44
      // public API (heif_image_create() + heif_image_add_plane()), so an
45
      // assert() here (compiled out under NDEBUG, i.e. every shipped build)
46
      // is not a guard: return the neutral, unsubsampled value instead of 0.
47
0
      return 1;
48
107k
  }
49
107k
}
50
51
52
uint8_t chroma_v_subsampling(heif_chroma c)
53
107k
{
54
107k
  switch (c) {
55
0
    case heif_chroma_monochrome:
56
36.7k
    case heif_chroma_444:
57
43.4k
    case heif_chroma_422:
58
43.4k
      return 1;
59
60
64.1k
    case heif_chroma_420:
61
64.1k
      return 2;
62
63
0
    case heif_chroma_interleaved_RGB:
64
0
    case heif_chroma_interleaved_RGBA:
65
0
    default:
66
      // See chroma_h_subsampling().
67
0
      return 1;
68
107k
  }
69
107k
}
70
71
72
uint32_t get_subsampled_size_h(uint32_t width,
73
                               heif_channel channel,
74
                               heif_chroma chroma,
75
                               scaling_mode mode)
76
110k
{
77
110k
  if (channel == heif_channel_Cb ||
78
93.2k
      channel == heif_channel_Cr) {
79
93.2k
    uint8_t chromaSubH = chroma_h_subsampling(chroma);
80
81
93.2k
    switch (mode) {
82
93.2k
      case scaling_mode::round_up:
83
        // NOLINTNEXTLINE(clang-analyzer-core.DivideZero)
84
93.2k
        return (width + chromaSubH - 1) / chromaSubH;
85
0
      case scaling_mode::round_down:
86
        // NOLINTNEXTLINE(clang-analyzer-core.DivideZero)
87
0
        return width / chromaSubH;
88
0
      case scaling_mode::is_divisible:
89
0
        assert(width % chromaSubH == 0);
90
0
        return width / chromaSubH;
91
0
      default:
92
0
        assert(false);
93
0
        return 0;
94
93.2k
    }
95
93.2k
  } else {
96
17.4k
    return width;
97
17.4k
  }
98
110k
}
99
100
101
uint32_t get_subsampled_size_v(uint32_t height,
102
                               heif_channel channel,
103
                               heif_chroma chroma,
104
                               scaling_mode mode)
105
110k
{
106
110k
  if (channel == heif_channel_Cb ||
107
93.2k
      channel == heif_channel_Cr) {
108
93.2k
    uint8_t chromaSubV = chroma_v_subsampling(chroma);
109
110
93.2k
    switch (mode) {
111
93.2k
      case scaling_mode::round_up:
112
        // NOLINTNEXTLINE(clang-analyzer-core.DivideZero)
113
93.2k
        return (height + chromaSubV - 1) / chromaSubV;
114
0
      case scaling_mode::round_down:
115
        // NOLINTNEXTLINE(clang-analyzer-core.DivideZero)
116
0
        return height / chromaSubV;
117
0
      case scaling_mode::is_divisible:
118
0
        assert(height % chromaSubV == 0);
119
0
        return height / chromaSubV;
120
0
      default:
121
0
        assert(false);
122
0
        return 0;
123
93.2k
    }
124
93.2k
  } else {
125
17.4k
    return height;
126
17.4k
  }
127
110k
}
128
129
130
void get_subsampled_size(uint32_t width, uint32_t height,
131
                         heif_channel channel,
132
                         heif_chroma chroma,
133
                         uint32_t* subsampled_width, uint32_t* subsampled_height)
134
110k
{
135
110k
  *subsampled_width = get_subsampled_size_h(width, channel, chroma, scaling_mode::round_up);
136
110k
  *subsampled_height = get_subsampled_size_v(height, channel, chroma, scaling_mode::round_up);
137
110k
}
138
139
140
141
uint8_t compute_avif_profile(int bits_per_pixel, heif_chroma chroma)
142
0
{
143
0
  if (bits_per_pixel <= 10 &&
144
0
      (chroma == heif_chroma_420 ||
145
0
       chroma == heif_chroma_monochrome)) {
146
0
    return 0;
147
0
  }
148
0
  else if (bits_per_pixel <= 10 &&
149
0
           chroma == heif_chroma_444) {
150
0
    return 1;
151
0
  }
152
0
  else {
153
0
    return 2;
154
0
  }
155
0
}
156
157
158
std::string fourcc_to_string(uint32_t code)
159
402k
{
160
402k
  std::string str("    ");
161
402k
  str[0] = static_cast<char>((code >> 24) & 0xFF);
162
402k
  str[1] = static_cast<char>((code >> 16) & 0xFF);
163
402k
  str[2] = static_cast<char>((code >> 8) & 0xFF);
164
402k
  str[3] = static_cast<char>((code >> 0) & 0xFF);
165
166
402k
  return str;
167
402k
}
168
169
170
Result<std::string> vector_to_string(const std::vector<uint8_t>& vec)
171
0
{
172
0
  if (vec.empty()) {
173
0
    return std::string{}; // return empty string
174
0
  }
175
176
0
  if (vec.back() != 0) {
177
0
    return Error{heif_error_Invalid_input,
178
0
                 heif_suberror_Unspecified,
179
0
                 "utf8string not null-terminated"};
180
0
  }
181
182
0
  for (size_t i=0;i<vec.size()-1;i++) {
183
0
    if (vec[i] == 0) {
184
0
      return Error{heif_error_Invalid_input,
185
0
                   heif_suberror_Unspecified,
186
0
                   "utf8string with null character"};
187
0
    }
188
0
  }
189
190
0
  return std::string(vec.begin(), vec.end()-1);
191
0
}