Skip to content

Commit

Permalink
Support for HEJ2 format
Browse files Browse the repository at this point in the history
  • Loading branch information
novomesk committed Dec 6, 2023
1 parent b7b4c0b commit 2532d56
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 21 deletions.
102 changes: 86 additions & 16 deletions src/heif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ 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;
bool HEIFHandler::m_hej2_decoder_available = false;

extern "C" {
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
Expand Down Expand Up @@ -59,12 +60,25 @@ HEIFHandler::HEIFHandler()

bool HEIFHandler::canRead() const
{
if (m_parseState == ParseHeicNotParsed && !canRead(device())) {
if (m_parseState == ParseHeicNotParsed) {
QIODevice *dev = device();
if (dev) {
const QByteArray header = dev->peek(28);

if (HEIFHandler::isSupportedBMFFType(header)) {
setFormat("heif");
return true;
}

if (HEIFHandler::isSupportedHEJ2(header)) {
setFormat("hej2");
return true;
}
}
return false;
}

if (m_parseState != ParseHeicError) {
setFormat("heif");
return true;
}
return false;
Expand Down Expand Up @@ -300,17 +314,6 @@ bool HEIFHandler::write_helper(const QImage &image)
return true;
}

bool HEIFHandler::canRead(QIODevice *device)
{
if (!device) {
qWarning("HEIFHandler::canRead() called with no device");
return false;
}

const QByteArray header = device->peek(28);
return HEIFHandler::isSupportedBMFFType(header);
}

bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
{
if (header.size() < 28) {
Expand Down Expand Up @@ -350,6 +353,22 @@ bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
return false;
}

bool HEIFHandler::isSupportedHEJ2(const QByteArray &header)
{
if (header.size() < 28) {
return false;
}

const char *buffer = header.constData();
if (qstrncmp(buffer + 4, "ftyp", 4) == 0) {
if (qstrncmp(buffer + 8, "j2ki", 4) == 0) {
return true;
}
}

return false;
}

QVariant HEIFHandler::option(ImageOption option) const
{
if (option == Quality) {
Expand Down Expand Up @@ -425,7 +444,7 @@ bool HEIFHandler::ensureDecoder()
}

const QByteArray buffer = device()->readAll();
if (!HEIFHandler::isSupportedBMFFType(buffer)) {
if (!HEIFHandler::isSupportedBMFFType(buffer) && !HEIFHandler::isSupportedHEJ2(buffer)) {
m_parseState = ParseHeicError;
return false;
}
Expand Down Expand Up @@ -814,6 +833,9 @@ bool HEIFHandler::isHeifDecoderAvailable()
}
#endif

#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#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;
Expand All @@ -839,6 +861,9 @@ bool HEIFHandler::isHeifEncoderAvailable()
}
#endif

#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#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;
Expand All @@ -853,6 +878,34 @@ bool HEIFHandler::isHeifEncoderAvailable()
return m_heif_encoder_available;
}

bool HEIFHandler::isHej2DecoderAvailable()
{
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);
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
#endif
m_plugins_queried = true;

#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_deinit();
}
#endif
}

return m_hej2_decoder_available;
}

void HEIFHandler::startHeifLib()
{
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
Expand Down Expand Up @@ -901,6 +954,15 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
}
return format_cap;
}

if (format == "hej2") {
Capabilities format_cap;
if (HEIFHandler::isHej2DecoderAvailable()) {
format_cap |= CanRead;
}
return format_cap;
}

if (!format.isEmpty()) {
return {};
}
Expand All @@ -909,8 +971,16 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
}

Capabilities cap;
if (device->isReadable() && HEIFHandler::canRead(device) && HEIFHandler::isHeifDecoderAvailable()) {
cap |= CanRead;
if (device->isReadable()) {
const QByteArray header = device->peek(28);

if (HEIFHandler::isSupportedBMFFType(header) && HEIFHandler::isHeifDecoderAvailable()) {
cap |= CanRead;
}

if (HEIFHandler::isSupportedHEJ2(header) && HEIFHandler::isHej2DecoderAvailable()) {
cap |= CanRead;
}
}

if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {
Expand Down
4 changes: 2 additions & 2 deletions src/heif.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"Keys": [ "heif", "heic" ],
"MimeTypes": [ "image/heif", "image/heif" ]
"Keys": [ "heif", "heic", "hej2" ],
"MimeTypes": [ "image/heif", "image/heif", "image/hej2k" ]
}
8 changes: 5 additions & 3 deletions src/heif_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@ class HEIFHandler : public QImageIOHandler
bool read(QImage *image) override;
bool write(const QImage &image) override;

static bool canRead(QIODevice *device);

QVariant option(ImageOption option) const override;
void setOption(ImageOption option, const QVariant &value) override;
bool supportsOption(ImageOption option) const override;

static bool isHeifDecoderAvailable();
static bool isHeifEncoderAvailable();
static bool isHej2DecoderAvailable();

private:
static bool isSupportedBMFFType(const QByteArray &header);
static bool isSupportedHEJ2(const QByteArray &header);

private:
bool ensureParsed() const;
bool ensureDecoder();

Expand All @@ -57,6 +58,7 @@ class HEIFHandler : public QImageIOHandler
static bool m_plugins_queried;
static bool m_heif_decoder_available;
static bool m_heif_encoder_available;
static bool m_hej2_decoder_available;

static QMutex &getHEIFHandlerMutex();
};
Expand Down

0 comments on commit 2532d56

Please sign in to comment.