Coverage Report

Created: 2025-06-16 07:00

/src/libheif/libheif/image-items/mask_image.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * HEIF mask image codec.
3
 *
4
 * Copyright (c) 2023 Dirk Farin <dirk.farin@gmail.com>
5
 * Copyright (c) 2023 Brad Hards <bradh@frogmouth.net>
6
 *
7
 * This file is part of libheif.
8
 *
9
 * libheif is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Lesser General Public License as
11
 * published by the Free Software Foundation, either version 3 of
12
 * the License, or (at your option) any later version.
13
 *
14
 * libheif is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include <cstdint>
24
#include <cstring>
25
#include <algorithm>
26
#include <map>
27
#include <cstring>
28
29
#include "libheif/heif.h"
30
#include "logging.h"
31
#include "mask_image.h"
32
#include "image_item.h"
33
#include "security_limits.h"
34
35
36
Error Box_mskC::parse(BitstreamRange& range, const heif_security_limits* limits)
37
4
{
38
4
  parse_full_box_header(range);
39
4
  m_bits_per_pixel = range.read8();
40
4
  return range.get_error();
41
4
}
42
43
std::string Box_mskC::dump(Indent& indent) const
44
0
{
45
0
  std::ostringstream sstr;
46
0
  sstr << Box::dump(indent);
47
0
  sstr << indent << "bits_per_pixel: " << ((int)m_bits_per_pixel) << "\n";
48
0
  return sstr.str();
49
0
}
50
51
Error Box_mskC::write(StreamWriter& writer) const
52
0
{
53
0
  size_t box_start = reserve_box_header_space(writer);
54
0
  writer.write8(m_bits_per_pixel);
55
0
  prepend_header(writer, box_start);
56
0
  return Error::Ok;
57
0
}
58
59
60
Error MaskImageCodec::decode_mask_image(const HeifContext* context,
61
                                        heif_item_id ID,
62
                                        std::shared_ptr<HeifPixelImage>& img,
63
                                        const std::vector<uint8_t>& data)
64
3
{
65
3
  auto image = context->get_image(ID, false);
66
3
  if (!image) {
67
0
    return {heif_error_Invalid_input,
68
0
            heif_suberror_Nonexisting_item_referenced};
69
0
  }
70
71
3
  std::shared_ptr<Box_ispe> ispe = image->get_property<Box_ispe>();
72
3
  std::shared_ptr<Box_mskC> mskC = image->get_property<Box_mskC>();
73
74
3
  uint32_t width = 0;
75
3
  uint32_t height = 0;
76
77
3
  if (ispe) {
78
3
    width = ispe->get_width();
79
3
    height = ispe->get_height();
80
81
3
    Error error = check_for_valid_image_size(context->get_security_limits(), width, height);
82
3
    if (error) {
83
0
      return error;
84
0
    }
85
3
  }
86
87
3
  if (!ispe || !mskC) {
88
3
    return Error(heif_error_Unsupported_feature,
89
3
                  heif_suberror_Unsupported_data_version,
90
3
                  "Missing required box for mask codec");
91
3
  }
92
93
0
  if ((mskC->get_bits_per_pixel() != 8) && (mskC->get_bits_per_pixel() != 16))
94
0
  {
95
0
    return Error(heif_error_Unsupported_feature,
96
0
                 heif_suberror_Unsupported_data_version,
97
0
                 "Unsupported bit depth for mask item");
98
0
  }
99
100
0
  if (data.size() < width * height) {
101
0
    return {heif_error_Invalid_input,
102
0
            heif_suberror_Unspecified,
103
0
            "Mask image data is too short"};
104
0
  }
105
106
0
  img = std::make_shared<HeifPixelImage>();
107
0
  img->create(width, height, heif_colorspace_monochrome, heif_chroma_monochrome);
108
0
  auto err = img->add_plane(heif_channel_Y, width, height, mskC->get_bits_per_pixel(),
109
0
                            context->get_security_limits());
110
0
  if (err) {
111
0
    return err;
112
0
  }
113
114
0
  size_t stride;
115
0
  uint8_t* dst = img->get_plane(heif_channel_Y, &stride);
116
0
  if (((uint32_t)stride) == width) {
117
0
    memcpy(dst, data.data(), data.size());
118
0
  }
119
0
  else
120
0
  {
121
0
    for (uint32_t i = 0; i < height; i++)
122
0
    {
123
0
      memcpy(dst + i * stride, data.data() + i * width, width);
124
0
    }
125
0
  }
126
0
  return Error::Ok;
127
0
}
128
129
130
Result<std::shared_ptr<HeifPixelImage>> ImageItem_mask::decode_compressed_image(const struct heif_decoding_options& options,
131
                                                                                bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
132
5
{
133
5
  std::shared_ptr<HeifPixelImage> img;
134
135
5
  std::vector<uint8_t> data;
136
137
  // image data, usually from 'mdat'
138
139
5
  Error error = get_file()->append_data_from_iloc(get_id(), data);
140
5
  if (error) {
141
2
    return error;
142
2
  }
143
144
3
  Error err = MaskImageCodec::decode_mask_image(get_context(),
145
3
                                                get_id(),
146
3
                                                img,
147
3
                                                data);
148
3
  if (err) {
149
3
    return err;
150
3
  }
151
0
  else {
152
0
    return img;
153
0
  }
154
3
}
155
156
157
Result<Encoder::CodedImageData> ImageItem_mask::encode(const std::shared_ptr<HeifPixelImage>& image,
158
                                                       struct heif_encoder* encoder,
159
                                                       const struct heif_encoding_options& options,
160
                                                       enum heif_image_input_class input_class)
161
0
{
162
0
  Encoder::CodedImageData codedImageData;
163
164
0
  if (image->get_colorspace() != heif_colorspace_monochrome)
165
0
  {
166
0
    return Error(heif_error_Unsupported_feature,
167
0
                 heif_suberror_Unsupported_data_version,
168
0
                 "Unsupported colourspace for mask region");
169
0
  }
170
171
0
  if (image->get_bits_per_pixel(heif_channel_Y) != 8)
172
0
  {
173
0
    return Error(heif_error_Unsupported_feature,
174
0
                 heif_suberror_Unsupported_data_version,
175
0
                 "Unsupported bit depth for mask region");
176
0
  }
177
178
  // TODO: we could add an option to lossless-compress this data
179
0
  std::vector<uint8_t> data;
180
0
  size_t src_stride;
181
0
  uint8_t* src_data = image->get_plane(heif_channel_Y, &src_stride);
182
183
0
  uint32_t w = image->get_width();
184
0
  uint32_t h = image->get_height();
185
186
0
  data.resize(w * h);
187
188
0
  if (w == (uint32_t)src_stride) {
189
0
    codedImageData.append(src_data, w*h);
190
0
  }
191
0
  else {
192
0
    for (uint32_t y = 0; y < h; y++) {
193
0
      codedImageData.append(src_data + y * src_stride, w);
194
0
    }
195
0
  }
196
197
0
  std::shared_ptr<Box_mskC> mskC = std::make_shared<Box_mskC>();
198
0
  mskC->set_bits_per_pixel(image->get_bits_per_pixel(heif_channel_Y));
199
0
  codedImageData.properties.push_back(mskC);
200
201
0
  return codedImageData;
202
0
}
203
204
205
int ImageItem_mask::get_luma_bits_per_pixel() const
206
10
{
207
10
  auto mskC = get_property<Box_mskC>();
208
10
  if (!mskC) {
209
10
    return -1;
210
10
  }
211
212
0
  return mskC->get_bits_per_pixel();
213
10
}