From 9ab64dbf223411098c21ec192f5181510db54ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Novomesk=C3=BD?= Date: Wed, 21 Sep 2022 02:30:51 +0200 Subject: [PATCH] heif: use heif_init/heif_deinit with libheif 1.13.0+ In recent libheif, application should use heif_init/heif_deinit calls. Unfortunately, these calls are not thread-safe in released 1.13.0 version yet (will be in the future) and HEIFHandler is often created in different threads. So we have to guard their use with a mutex. --- src/imageformats/heif.cpp | 126 ++++++++++++++++++++++++++++++++++++-- src/imageformats/heif_p.h | 16 +++++ 2 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/imageformats/heif.cpp b/src/imageformats/heif.cpp index 0d4579f..eea93c7 100644 --- a/src/imageformats/heif.cpp +++ b/src/imageformats/heif.cpp @@ -52,6 +52,11 @@ private: } // namespace +size_t HEIFHandler::m_initialized_count = 0; +bool HEIFHandler::m_plugins_queried = false; +bool HEIFHandler::m_heif_decoder_available = false; +bool HEIFHandler::m_heif_encoder_available = false; + HEIFHandler::HEIFHandler() : m_parseState(ParseHeicNotParsed) , m_quality(100) @@ -88,6 +93,21 @@ bool HEIFHandler::write(const QImage &image) return false; } +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + startHeifLib(); +#endif + + bool success = write_helper(image); + +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + finishHeifLib(); +#endif + + return success; +} + +bool HEIFHandler::write_helper(const QImage &image) +{ int save_depth; // 8 or 10bit per channel QImage::Format tmpformat; // format for temporary image const bool save_alpha = image.hasAlphaChannel(); @@ -356,8 +376,18 @@ bool HEIFHandler::ensureParsed() const HEIFHandler *that = const_cast(this); - return that->ensureDecoder(); +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + startHeifLib(); +#endif + + bool success = that->ensureDecoder(); + +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + finishHeifLib(); +#endif + return success; } + bool HEIFHandler::ensureDecoder() { if (m_parseState != ParseHeicNotParsed) { @@ -696,14 +726,100 @@ bool HEIFHandler::ensureDecoder() return true; } +bool HEIFHandler::isHeifDecoderAvailable() +{ + QMutexLocker locker(&getHEIFHandlerMutex()); + + if (!m_plugins_queried) { +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + if (m_initialized_count == 0) { + heif_init(nullptr); + } +#endif + + m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC); + m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC); + m_plugins_queried = true; + +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + if (m_initialized_count == 0) { + heif_deinit(); + } +#endif + } + + return m_heif_decoder_available; +} + +bool HEIFHandler::isHeifEncoderAvailable() +{ + QMutexLocker locker(&getHEIFHandlerMutex()); + + if (!m_plugins_queried) { +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + if (m_initialized_count == 0) { + heif_init(nullptr); + } +#endif + + m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC); + m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC); + m_plugins_queried = true; + +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + if (m_initialized_count == 0) { + heif_deinit(); + } +#endif + } + + return m_heif_encoder_available; +} + +void HEIFHandler::startHeifLib() +{ +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + QMutexLocker locker(&getHEIFHandlerMutex()); + + if (m_initialized_count == 0) { + heif_init(nullptr); + } + + m_initialized_count++; +#endif +} + +void HEIFHandler::finishHeifLib() +{ +#if LIBHEIF_HAVE_VERSION(1, 13, 0) + QMutexLocker locker(&getHEIFHandlerMutex()); + + if (m_initialized_count == 0) { + return; + } + + m_initialized_count--; + if (m_initialized_count == 0) { + heif_deinit(); + } + +#endif +} + +QMutex &HEIFHandler::getHEIFHandlerMutex() +{ + static QMutex heif_handler_mutex; + return heif_handler_mutex; +} + QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const { if (format == "heif" || format == "heic") { Capabilities format_cap; - if (heif_have_decoder_for_format(heif_compression_HEVC)) { + if (HEIFHandler::isHeifDecoderAvailable()) { format_cap |= CanRead; } - if (heif_have_encoder_for_format(heif_compression_HEVC)) { + if (HEIFHandler::isHeifEncoderAvailable()) { format_cap |= CanWrite; } return format_cap; @@ -716,11 +832,11 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q } Capabilities cap; - if (device->isReadable() && HEIFHandler::canRead(device) && heif_have_decoder_for_format(heif_compression_HEVC)) { + if (device->isReadable() && HEIFHandler::canRead(device) && HEIFHandler::isHeifDecoderAvailable()) { cap |= CanRead; } - if (device->isWritable() && heif_have_encoder_for_format(heif_compression_HEVC)) { + if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) { cap |= CanWrite; } return cap; diff --git a/src/imageformats/heif_p.h b/src/imageformats/heif_p.h index ec57705..9fbe0ea 100644 --- a/src/imageformats/heif_p.h +++ b/src/imageformats/heif_p.h @@ -13,6 +13,7 @@ #include #include #include +#include class HEIFHandler : public QImageIOHandler { @@ -29,6 +30,9 @@ public: void setOption(ImageOption option, const QVariant &value) override; bool supportsOption(ImageOption option) const override; + static bool isHeifDecoderAvailable(); + static bool isHeifEncoderAvailable(); + private: static bool isSupportedBMFFType(const QByteArray &header); bool ensureParsed() const; @@ -43,6 +47,18 @@ private: ParseHeicState m_parseState; int m_quality; QImage m_current_image; + + bool write_helper(const QImage &image); + + static void startHeifLib(); + static void finishHeifLib(); + static size_t m_initialized_count; + + static bool m_plugins_queried; + static bool m_heif_decoder_available; + static bool m_heif_encoder_available; + + static QMutex &getHEIFHandlerMutex(); }; class HEIFPlugin : public QImageIOPlugin