mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-28 00:30:23 -04:00
HEIF plug-in extended to support HEJ2 format
HEJ2 is JPEG 2000 codec encapsulated in HEIF container. Only HEJ2 reading is implemented.
This commit is contained in:
parent
e90bca4924
commit
43c80793ac
@ -99,6 +99,12 @@ if (LibHeif_FOUND)
|
|||||||
kimageformats_write_tests(FUZZ 1
|
kimageformats_write_tests(FUZZ 1
|
||||||
heif-nodatacheck-lossless
|
heif-nodatacheck-lossless
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
|
||||||
|
kimageformats_read_tests(FUZZ 1
|
||||||
|
hej2
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (LibJXL_FOUND AND LibJXLThreads_FOUND)
|
if (LibJXL_FOUND AND LibJXLThreads_FOUND)
|
||||||
|
BIN
autotests/read/hej2/rgb_kcolorchooser.hej2
Normal file
BIN
autotests/read/hej2/rgb_kcolorchooser.hej2
Normal file
Binary file not shown.
BIN
autotests/read/hej2/rgb_kcolorchooser.png
Normal file
BIN
autotests/read/hej2/rgb_kcolorchooser.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
autotests/read/hej2/rgba_kolourpaint.hej2
Normal file
BIN
autotests/read/hej2/rgba_kolourpaint.hej2
Normal file
Binary file not shown.
BIN
autotests/read/hej2/rgba_kolourpaint.png
Normal file
BIN
autotests/read/hej2/rgba_kolourpaint.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
@ -22,6 +22,7 @@ size_t HEIFHandler::m_initialized_count = 0;
|
|||||||
bool HEIFHandler::m_plugins_queried = false;
|
bool HEIFHandler::m_plugins_queried = false;
|
||||||
bool HEIFHandler::m_heif_decoder_available = false;
|
bool HEIFHandler::m_heif_decoder_available = false;
|
||||||
bool HEIFHandler::m_heif_encoder_available = false;
|
bool HEIFHandler::m_heif_encoder_available = false;
|
||||||
|
bool HEIFHandler::m_hej2_decoder_available = false;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
|
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
|
||||||
@ -59,12 +60,25 @@ HEIFHandler::HEIFHandler()
|
|||||||
|
|
||||||
bool HEIFHandler::canRead() const
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_parseState != ParseHeicError) {
|
if (m_parseState != ParseHeicError) {
|
||||||
setFormat("heif");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -300,17 +314,6 @@ bool HEIFHandler::write_helper(const QImage &image)
|
|||||||
return true;
|
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)
|
bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
|
||||||
{
|
{
|
||||||
if (header.size() < 28) {
|
if (header.size() < 28) {
|
||||||
@ -350,6 +353,22 @@ bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
|
|||||||
return false;
|
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
|
QVariant HEIFHandler::option(ImageOption option) const
|
||||||
{
|
{
|
||||||
if (option == Quality) {
|
if (option == Quality) {
|
||||||
@ -425,7 +444,7 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray buffer = device()->readAll();
|
const QByteArray buffer = device()->readAll();
|
||||||
if (!HEIFHandler::isSupportedBMFFType(buffer)) {
|
if (!HEIFHandler::isSupportedBMFFType(buffer) && !HEIFHandler::isSupportedHEJ2(buffer)) {
|
||||||
m_parseState = ParseHeicError;
|
m_parseState = ParseHeicError;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -814,6 +833,9 @@ bool HEIFHandler::isHeifDecoderAvailable()
|
|||||||
}
|
}
|
||||||
#endif
|
#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_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
||||||
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
||||||
m_plugins_queried = true;
|
m_plugins_queried = true;
|
||||||
@ -839,6 +861,9 @@ bool HEIFHandler::isHeifEncoderAvailable()
|
|||||||
}
|
}
|
||||||
#endif
|
#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_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
||||||
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
||||||
m_plugins_queried = true;
|
m_plugins_queried = true;
|
||||||
@ -853,6 +878,34 @@ bool HEIFHandler::isHeifEncoderAvailable()
|
|||||||
return m_heif_encoder_available;
|
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()
|
void HEIFHandler::startHeifLib()
|
||||||
{
|
{
|
||||||
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
@ -901,6 +954,15 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
|
|||||||
}
|
}
|
||||||
return format_cap;
|
return format_cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (format == "hej2") {
|
||||||
|
Capabilities format_cap;
|
||||||
|
if (HEIFHandler::isHej2DecoderAvailable()) {
|
||||||
|
format_cap |= CanRead;
|
||||||
|
}
|
||||||
|
return format_cap;
|
||||||
|
}
|
||||||
|
|
||||||
if (!format.isEmpty()) {
|
if (!format.isEmpty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -909,8 +971,16 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
Capabilities cap;
|
Capabilities cap;
|
||||||
if (device->isReadable() && HEIFHandler::canRead(device) && HEIFHandler::isHeifDecoderAvailable()) {
|
if (device->isReadable()) {
|
||||||
cap |= CanRead;
|
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()) {
|
if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"Keys": [ "heif", "heic" ],
|
"Keys": [ "heif", "heic", "hej2" ],
|
||||||
"MimeTypes": [ "image/heif", "image/heif" ]
|
"MimeTypes": [ "image/heif", "image/heif", "image/hej2k" ]
|
||||||
}
|
}
|
||||||
|
@ -24,17 +24,18 @@ public:
|
|||||||
bool read(QImage *image) override;
|
bool read(QImage *image) override;
|
||||||
bool write(const QImage &image) override;
|
bool write(const QImage &image) override;
|
||||||
|
|
||||||
static bool canRead(QIODevice *device);
|
|
||||||
|
|
||||||
QVariant option(ImageOption option) const override;
|
QVariant option(ImageOption option) const override;
|
||||||
void setOption(ImageOption option, const QVariant &value) override;
|
void setOption(ImageOption option, const QVariant &value) override;
|
||||||
bool supportsOption(ImageOption option) const override;
|
bool supportsOption(ImageOption option) const override;
|
||||||
|
|
||||||
static bool isHeifDecoderAvailable();
|
static bool isHeifDecoderAvailable();
|
||||||
static bool isHeifEncoderAvailable();
|
static bool isHeifEncoderAvailable();
|
||||||
|
static bool isHej2DecoderAvailable();
|
||||||
|
|
||||||
|
static bool isSupportedBMFFType(const QByteArray &header);
|
||||||
|
static bool isSupportedHEJ2(const QByteArray &header);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool isSupportedBMFFType(const QByteArray &header);
|
|
||||||
bool ensureParsed() const;
|
bool ensureParsed() const;
|
||||||
bool ensureDecoder();
|
bool ensureDecoder();
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ private:
|
|||||||
static bool m_plugins_queried;
|
static bool m_plugins_queried;
|
||||||
static bool m_heif_decoder_available;
|
static bool m_heif_decoder_available;
|
||||||
static bool m_heif_encoder_available;
|
static bool m_heif_encoder_available;
|
||||||
|
static bool m_hej2_decoder_available;
|
||||||
|
|
||||||
static QMutex &getHEIFHandlerMutex();
|
static QMutex &getHEIFHandlerMutex();
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user