mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-06-15 11:19:19 -04:00
HEIF: use heif_reader for random access devices
This commit is contained in:
committed by
Mirco Miranda
parent
7edf807082
commit
8768a8cf97
@@ -14,6 +14,7 @@
|
||||
#include <libheif/heif_properties.h>
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QImageReader>
|
||||
#include <QLoggingCategory>
|
||||
#include <QPointF>
|
||||
#include <QSysInfo>
|
||||
@@ -64,6 +65,58 @@ bool HEIFHandler::m_hej2_decoder_available = false;
|
||||
bool HEIFHandler::m_hej2_encoder_available = false;
|
||||
bool HEIFHandler::m_avci_decoder_available = false;
|
||||
|
||||
/*!
|
||||
* \brief create_heif_reader_for_qiodevice
|
||||
* Create a heif_reader structure that wraps a QIODevice for streaming
|
||||
* \return heif_reader structure with callbacks delegating to QIODevice
|
||||
*/
|
||||
static heif_reader create_heif_reader_for_qiodevice()
|
||||
{
|
||||
heif_reader reader = {};
|
||||
|
||||
reader.reader_api_version = 1;
|
||||
|
||||
reader.get_position = [](void* userdata) -> int64_t {
|
||||
QIODevice* device = static_cast<QIODevice*>(userdata);
|
||||
return device->pos();
|
||||
};
|
||||
|
||||
reader.read = [](void* data, size_t size, void* userdata) -> int {
|
||||
QIODevice* device = static_cast<QIODevice*>(userdata);
|
||||
qint64 bytesRead = device->read(static_cast<char*>(data), size);
|
||||
if (bytesRead == -1) {
|
||||
return -1; // Error
|
||||
}
|
||||
if (bytesRead != size) {
|
||||
// We expected to read 'size' bytes but got less.
|
||||
// This is an error because we should have known the size.
|
||||
return -1; // Error
|
||||
}
|
||||
return 0; // Success
|
||||
};
|
||||
|
||||
reader.seek = [](int64_t position, void* userdata) -> int {
|
||||
QIODevice* device = static_cast<QIODevice*>(userdata);
|
||||
return device->seek(position) ? 0 : -1;
|
||||
};
|
||||
|
||||
reader.wait_for_file_size = [](int64_t target_size, void* userdata) -> heif_reader_grow_status {
|
||||
QIODevice* device = static_cast<QIODevice*>(userdata);
|
||||
if (target_size <= device->size()) {
|
||||
return heif_reader_grow_status_size_reached;
|
||||
}
|
||||
return heif_reader_grow_status_size_beyond_eof;
|
||||
};
|
||||
|
||||
// Version 2 functions set to NULL as we don't need them for simple streaming
|
||||
reader.request_range = nullptr;
|
||||
reader.preload_range_hint = nullptr;
|
||||
reader.release_file_range = nullptr;
|
||||
reader.release_error_msg = nullptr; // We use static error strings
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
|
||||
{
|
||||
@@ -621,17 +674,48 @@ bool HEIFHandler::ensureDecoder()
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray buffer = device()->readAll();
|
||||
QIODevice *dev = device();
|
||||
if (dev == nullptr) {
|
||||
qCCritical(LOG_HEIFPLUGIN) << "create_heif_reader_for_qiodevice error: device is null";
|
||||
m_parseState = ParseHeicError;
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray buffer = dev->peek(28);
|
||||
if (!HEIFHandler::isSupportedBMFFType(buffer) && !HEIFHandler::isSupportedHEJ2(buffer) && !HEIFHandler::isSupportedAVCI(buffer)) {
|
||||
m_parseState = ParseHeicError;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct heif_context *ctx = heif_context_alloc();
|
||||
struct heif_error err = heif_context_read_from_memory(ctx, static_cast<const void *>(buffer.constData()), buffer.size(), nullptr);
|
||||
struct heif_error err;
|
||||
|
||||
#if LIBHEIF_HAVE_VERSION(1, 19, 1)
|
||||
if (auto heif_limits = heif_context_get_security_limits(ctx)) {
|
||||
heif_limits->max_image_size_pixels = quint64(HEIF_MAX_IMAGE_WIDTH) * HEIF_MAX_IMAGE_HEIGHT;
|
||||
heif_limits->max_memory_block_size = quint64(QImageReader::allocationLimit()) * 1024 * 1024;
|
||||
#if LIBHEIF_HAVE_VERSION(1, 20, 0)
|
||||
heif_limits->max_total_memory = heif_limits->max_memory_block_size;
|
||||
#endif
|
||||
err = heif_context_set_security_limits(ctx, heif_limits);
|
||||
if (err.code) {
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_context_set_security_limits error:" << err.message;
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dev->isSequential()) {
|
||||
buffer = deviceRead(dev, kMaxQVectorSize);
|
||||
err = heif_context_read_from_memory(ctx, static_cast<const void *>(buffer.constData()), buffer.size(), nullptr);
|
||||
} else {
|
||||
heif_reader reader = create_heif_reader_for_qiodevice();
|
||||
err = heif_context_read_from_reader(ctx, &reader, dev, nullptr);
|
||||
}
|
||||
if (err.code) {
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_context_read_from_memory error:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_context_read_from_reader error:" << err.message;
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user