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.
This commit is contained in:
Daniel Novomeský 2022-09-21 02:30:51 +02:00
parent 20f74ce5e6
commit 9ab64dbf22
2 changed files with 137 additions and 5 deletions

View File

@ -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<HEIFHandler *>(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;

View File

@ -13,6 +13,7 @@
#include <QByteArray>
#include <QImage>
#include <QImageIOPlugin>
#include <QMutex>
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