Coverage Report

Created: 2025-10-12 06:18

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