Coverage Report

Created: 2026-02-14 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/init.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2022 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 "init.h"
22
#include "libheif/heif.h"
23
#include "error.h"
24
#include "plugin_registry.h"
25
#include "common_utils.h"
26
#include "color-conversion/colorconversion.h"
27
28
#if ENABLE_MULTITHREADING_SUPPORT
29
30
#include <mutex>
31
32
#endif
33
34
#if defined(_WIN32)
35
#include "plugins_windows.h"
36
#else
37
38
#include "plugins_unix.h"
39
40
#endif
41
42
#define STRINGIFY2(x) #x
43
#define STRINGIFY(x) STRINGIFY2(x)
44
45
void heif_unload_all_plugins();
46
47
#if ENABLE_PLUGIN_LOADING
48
49
void heif_unregister_encoder_plugin(const heif_encoder_plugin* plugin);
50
51
std::vector<std::string> get_plugin_paths()
52
{
53
  std::vector<std::string> plugin_paths;
54
55
#if defined(_WIN32)
56
  plugin_paths = get_plugin_directories_from_environment_variable_windows();
57
#else
58
  plugin_paths = get_plugin_directories_from_environment_variable_unix();
59
#endif
60
61
  if (plugin_paths.empty()) {
62
    plugin_paths.push_back(LIBHEIF_PLUGIN_DIRECTORY);
63
  }
64
65
  return plugin_paths;
66
}
67
68
std::vector<std::string> list_all_potential_plugins_in_directory(const char* directory)
69
{
70
#if defined(_WIN32)
71
  return list_all_potential_plugins_in_directory_windows(directory);
72
#else
73
  return list_all_potential_plugins_in_directory_unix(directory);
74
#endif
75
}
76
77
#else
78
std::vector<std::string> get_plugin_paths()
79
0
{
80
0
  return {};
81
0
}
82
#endif
83
84
85
static int heif_library_initialization_count = 0;
86
static bool default_plugins_registered = true; // because they are implicitly registered at startup
87
88
89
#if ENABLE_MULTITHREADING_SUPPORT
90
91
static std::recursive_mutex& heif_init_mutex()
92
9
{
93
9
  static std::recursive_mutex init_mutex;
94
9
  return init_mutex;
95
9
}
96
97
#endif
98
99
100
void load_plugins_if_not_initialized_yet()
101
15.5k
{
102
15.5k
  if (heif_library_initialization_count == 0) {
103
0
    heif_init(nullptr);
104
0
  }
105
15.5k
}
106
107
108
heif_error heif_init(heif_init_params*)
109
9
{
110
9
#if ENABLE_MULTITHREADING_SUPPORT
111
9
  std::lock_guard<std::recursive_mutex> lock(heif_init_mutex());
112
9
#endif
113
114
9
  if (heif_library_initialization_count == 0) {
115
116
9
    ColorConversionPipeline::init_ops();
117
118
    // --- initialize builtin plugins
119
120
9
    if (!default_plugins_registered) {
121
0
      register_default_plugins();
122
0
    }
123
124
#if ENABLE_PLUGIN_LOADING
125
    struct heif_error err{};
126
    std::vector<std::string> plugin_paths = get_plugin_paths();
127
128
    for (const auto& dir : plugin_paths) {
129
      err = heif_load_plugins(dir.c_str(), nullptr, nullptr, 0);
130
      if (err.code != 0) {
131
        return err;
132
      }
133
    }
134
#endif
135
9
  }
136
137
  // Note: it is important that we increase the counter AFTER initialization such that
138
  // 'load_plugins_if_not_initialized_yet()' can check this without having to lock the mutex.
139
9
  heif_library_initialization_count++;
140
141
9
  return {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess};
142
9
}
143
144
145
void heif_deinit()
146
0
{
147
0
#if ENABLE_MULTITHREADING_SUPPORT
148
0
  std::lock_guard<std::recursive_mutex> lock(heif_init_mutex());
149
0
#endif
150
151
0
  if (heif_library_initialization_count == 0) {
152
    // This case should never happen (heif_deinit() is called more often than heif_init()).
153
0
    return;
154
0
  }
155
156
0
  if (heif_library_initialization_count == 1) {
157
0
    heif_unregister_decoder_plugins();
158
0
    heif_unregister_encoder_plugins();
159
0
    default_plugins_registered = false;
160
161
0
    heif_unload_all_plugins();
162
163
0
    ColorConversionPipeline::release_ops();
164
0
  }
165
166
  // Note: contrary to heif_init() I think it does not matter whether we decrease the counter before or after deinitialization.
167
  // If the client application calls heif_deinit() in parallel to some other libheif function, it is really broken.
168
0
  heif_library_initialization_count--;
169
0
}
170
171
172
// This could be inside ENABLE_PLUGIN_LOADING, but the "include-what-you-use" checker cannot process this.
173
#include <vector>
174
#include <string>
175
#include <cstring>
176
177
#if ENABLE_PLUGIN_LOADING
178
179
#if defined(_WIN32)
180
typedef PluginLibrary_Windows PluginLibrary_SysDep;
181
#else
182
typedef PluginLibrary_Unix PluginLibrary_SysDep;
183
#endif
184
185
186
struct loaded_plugin
187
{
188
  PluginLibrary_SysDep plugin_library_handle;
189
  struct heif_plugin_info* info = nullptr;
190
  int openCnt = 0;
191
};
192
193
static std::vector<loaded_plugin> sLoadedPlugins;
194
195
MAYBE_UNUSED heif_error error_dlopen{heif_error_Plugin_loading_error, heif_suberror_Plugin_loading_error, "Cannot open plugin (dlopen)."};
196
MAYBE_UNUSED heif_error error_plugin_not_loaded{heif_error_Plugin_loading_error, heif_suberror_Plugin_is_not_loaded, "Trying to remove a plugin that is not loaded."};
197
MAYBE_UNUSED heif_error error_cannot_read_plugin_directory{heif_error_Plugin_loading_error, heif_suberror_Cannot_read_plugin_directory, "Cannot read plugin directory."};
198
199
MAYBE_UNUSED static void unregister_plugin(const heif_plugin_info* info)
200
{
201
  switch (info->type) {
202
    case heif_plugin_type_encoder: {
203
      auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(info->plugin);
204
      heif_unregister_encoder_plugin(encoder_plugin);
205
      break;
206
    }
207
    case heif_plugin_type_decoder: {
208
      // TODO
209
    }
210
  }
211
}
212
213
214
struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin)
215
{
216
#if ENABLE_MULTITHREADING_SUPPORT
217
  std::lock_guard<std::recursive_mutex> lock(heif_init_mutex());
218
#endif
219
220
  PluginLibrary_SysDep plugin;
221
  auto err = plugin.load_from_file(filename);
222
  if (err.code) {
223
    return err;
224
  }
225
226
  heif_plugin_info* plugin_info = plugin.get_plugin_info();
227
228
  // --- check whether the plugin is already loaded
229
  // If yes, return pointer to existing plugin.
230
231
  for (auto& p : sLoadedPlugins) {
232
    if (p.plugin_library_handle == plugin) {
233
      if (out_plugin) {
234
        *out_plugin = p.info;
235
        p.openCnt++;
236
        return heif_error_ok;
237
      }
238
    }
239
  }
240
241
  loaded_plugin loadedPlugin;
242
  loadedPlugin.plugin_library_handle = plugin;
243
  loadedPlugin.openCnt = 1;
244
  loadedPlugin.info = plugin_info;
245
  sLoadedPlugins.push_back(loadedPlugin);
246
247
  *out_plugin = plugin_info;
248
249
  switch (loadedPlugin.info->type) {
250
    case heif_plugin_type_encoder: {
251
      auto* encoder_plugin = static_cast<const heif_encoder_plugin*>(plugin_info->plugin);
252
      if (encoder_plugin->plugin_api_version < heif_encoder_plugin_minimum_version) {
253
        return {heif_error_Plugin_loading_error,
254
                heif_suberror_Unsupported_plugin_version,
255
                "Encoder plugin needs to be at least version " STRINGIFY(heif_encoder_plugin_latest_version)
256
        };
257
      }
258
259
      if (encoder_plugin->plugin_api_version >= 4 &&
260
          encoder_plugin->minimum_required_libheif_version > heif_get_version_number()) {
261
        return {
262
          heif_error_Plugin_loading_error,
263
          heif_suberror_Unsupported_plugin_version,
264
          "Encoder plugin requires at least libheif version " LIBHEIF_VERSION
265
        };
266
      }
267
268
      struct heif_error err = heif_register_encoder_plugin(encoder_plugin);
269
      if (err.code) {
270
        return err;
271
      }
272
      break;
273
    }
274
275
    case heif_plugin_type_decoder: {
276
      auto* decoder_plugin = static_cast<const heif_decoder_plugin*>(plugin_info->plugin);
277
      if (decoder_plugin->plugin_api_version < heif_decoder_plugin_minimum_version) {
278
        return {heif_error_Plugin_loading_error,
279
                heif_suberror_Unsupported_plugin_version,
280
                "Decoder plugin needs to be at least version " STRINGIFY(heif_decoder_plugin_latest_version)
281
        };
282
      }
283
284
      if (decoder_plugin->plugin_api_version >= 4 &&
285
          decoder_plugin->minimum_required_libheif_version > heif_get_version_number()) {
286
        return {
287
          heif_error_Plugin_loading_error,
288
          heif_suberror_Unsupported_plugin_version,
289
          "Decoder plugin requires at least libheif version " LIBHEIF_VERSION
290
        };
291
      }
292
293
      struct heif_error err = heif_register_decoder_plugin(decoder_plugin);
294
      if (err.code) {
295
        return err;
296
      }
297
      break;
298
    }
299
  }
300
301
  return heif_error_ok;
302
}
303
304
struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin)
305
{
306
#if ENABLE_MULTITHREADING_SUPPORT
307
  std::lock_guard<std::recursive_mutex> lock(heif_init_mutex());
308
#endif
309
310
  for (size_t i = 0; i < sLoadedPlugins.size(); i++) {
311
    auto& p = sLoadedPlugins[i];
312
313
    if (p.info == plugin) {
314
      p.plugin_library_handle.release();
315
      p.openCnt--;
316
317
      if (p.openCnt == 0) {
318
        unregister_plugin(plugin);
319
320
        sLoadedPlugins[i] = sLoadedPlugins.back();
321
        sLoadedPlugins.pop_back();
322
      }
323
324
      return heif_error_ok;
325
    }
326
  }
327
328
  return error_plugin_not_loaded;
329
}
330
331
void heif_unload_all_plugins()
332
{
333
#if ENABLE_MULTITHREADING_SUPPORT
334
  std::lock_guard<std::recursive_mutex> lock(heif_init_mutex());
335
#endif
336
337
  for (auto& p : sLoadedPlugins) {
338
    unregister_plugin(p.info);
339
340
    for (int i = 0; i < p.openCnt; i++) {
341
      p.plugin_library_handle.release();
342
    }
343
  }
344
345
  sLoadedPlugins.clear();
346
}
347
348
349
struct heif_error heif_load_plugins(const char* directory,
350
                                    const struct heif_plugin_info** out_plugins,
351
                                    int* out_nPluginsLoaded,
352
                                    int output_array_size)
353
{
354
  auto libraryFiles = list_all_potential_plugins_in_directory(directory);
355
356
  int nPlugins = 0;
357
358
  // Loading the plugins may return several errors, but we can only return one of them,
359
  // which is remembered here.
360
  heif_error err_result = heif_error_ok;
361
362
  for (const auto& filename : libraryFiles) {
363
    const struct heif_plugin_info* info = nullptr;
364
    auto err = heif_load_plugin(filename.c_str(), &info);
365
    if (err.code == 0) {
366
      if (out_plugins) {
367
        if (nPlugins == output_array_size) {
368
          break;
369
        }
370
371
        out_plugins[nPlugins] = info;
372
      }
373
374
      nPlugins++;
375
    }
376
    else {
377
      // remember error
378
      err_result = err;
379
    }
380
  }
381
382
  if (nPlugins < output_array_size && out_plugins) {
383
    out_plugins[nPlugins] = nullptr;
384
  }
385
386
  if (out_nPluginsLoaded) {
387
    *out_nPluginsLoaded = nPlugins;
388
  }
389
390
  return err_result;
391
}
392
393
#else
394
static heif_error heif_error_plugins_unsupported{heif_error_Unsupported_feature, heif_suberror_Unspecified, "Plugins are not supported"};
395
396
struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin)
397
0
{
398
0
  return heif_error_plugins_unsupported;
399
0
}
400
401
402
struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin)
403
0
{
404
0
  return heif_error_plugins_unsupported;
405
0
}
406
407
408
0
void heif_unload_all_plugins() {}
409
410
struct heif_error heif_load_plugins(const char* directory,
411
                                    const struct heif_plugin_info** out_plugins,
412
                                    int* out_nPluginsLoaded,
413
                                    int output_array_size)
414
0
{
415
0
  if (out_nPluginsLoaded) {
416
0
    *out_nPluginsLoaded = 0;
417
0
  }
418
419
0
  return heif_error_plugins_unsupported;
420
0
}
421
422
#endif
423
424
425
const char* const* heif_get_plugin_directories()
426
0
{
427
0
  auto plugin_paths = get_plugin_paths();
428
0
  size_t n = plugin_paths.size();
429
430
0
  auto out_paths = new char* [n + 1];
431
0
  for (size_t i = 0; i < n; i++) {
432
0
    out_paths[i] = new char[plugin_paths[i].size() + 1];
433
0
    strcpy(out_paths[i], plugin_paths[i].c_str());
434
0
  }
435
436
0
  out_paths[n] = nullptr;
437
438
0
  return out_paths;
439
0
}
440
441
442
void heif_free_plugin_directories(const char* const* paths)
443
0
{
444
0
  for (int i = 0; paths[i]; i++) {
445
0
    delete[] paths[i];
446
0
  }
447
448
0
  delete[] paths;
449
0
}