C
Qt Quick Ultralite imagedecoder Example
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
#include "desktopimagedecoder.h"
#include <QBuffer>
#include <QImage>
#include <QImageReader>
#include <platforminterface/log.h>
static QImage::Format convertColorFormat(Qul::PixelFormat pixelFormat)
{
switch (pixelFormat) {
case Qul::PixelFormat_ARGB32:
return QImage::Format_ARGB32;
case Qul::PixelFormat_ARGB32_Premultiplied:
return QImage::Format_ARGB32_Premultiplied;
case Qul::PixelFormat_RGB32:
return QImage::Format_RGB32;
case Qul::PixelFormat_RGB888:
return QImage::Format_RGB888;
case Qul::PixelFormat_RGB16:
return QImage::Format_RGB16;
case Qul::PixelFormat_Alpha8:
return QImage::Format_Alpha8;
case Qul::PixelFormat_Alpha1:
return QImage::Format_Mono;
case Qul::PixelFormat_ARGB4444: // Unsupported?
return QImage::Format_Invalid;
case Qul::PixelFormat_ARGB4444_Premultiplied:
return QImage::Format_ARGB4444_Premultiplied;
case Qul::PixelFormat_RGB332: // Unsupported?
return QImage::Format_Invalid;
case Qul::PixelFormat_RLE_ARGB32:
case Qul::PixelFormat_RLE_ARGB32_Premultiplied:
case Qul::PixelFormat_RLE_RGB32:
case Qul::PixelFormat_RLE_RGB888:
return QImage::Format_Invalid;
default:
return QImage::Format_Invalid;
}
}
DesktopImageDecoder::DesktopImageDecoder() {}
DesktopImageDecoder::~DesktopImageDecoder() {}
static bool probe(const unsigned char *data, uint32_t size)
{
// Detect JPG images
// FF D8 FF E0 xx xx 4A 46
// FF D8 FF E1 xx xx 45 78
// FF D8 FF E8 xx xx 53 50
if (size < 8)
return false;
if (data[0] != 0xff || data[1] != 0xd8 || data[2] != 0xff)
return false;
if (data[3] == 0xe0 && data[6] == 0x4a && data[7] == 0x46)
return true;
else if (data[3] == 0xe1 && data[6] == 0x45 && data[7] == 0x78)
return true;
else if (data[3] == 0xe8 && data[6] == 0x53 && data[7] == 0x50)
return true;
return false;
}
bool DesktopImageDecoder::imageInformation(RequestDataCallback &callback,
int16_t *width,
int16_t *height,
Qul::PixelFormat *actualPixelFormat,
Qul::PixelFormat optimalOpaquePixelFormat,
Qul::PixelFormat optimalAlphaPixelFormat)
{
QUL_UNUSED(optimalOpaquePixelFormat);
QUL_UNUSED(optimalAlphaPixelFormat);
{
unsigned char buffer[8];
if (callback.readData(buffer, 0, sizeof(buffer)) != sizeof(buffer))
return false;
if (!probe(buffer, sizeof(buffer)))
return false;
}
uint32_t offset = 0;
while (1) {
unsigned char marker;
do {
if (callback.readData(&marker, offset, sizeof(marker)) != sizeof(marker))
return false;
offset += sizeof(marker);
} while (marker == 0xff); // Ignore start and padding bytes
if (marker > 0xd0 && marker <= 0xd8)
continue;
if (marker == 0xd9)
return false; // EOI without finding a size block
if (marker == 0x01) // TEM
continue;
if (marker == 0xc0) {
unsigned char buffer[8];
if (callback.readData(buffer, offset, sizeof(buffer)) != sizeof(buffer))
return false;
offset += sizeof(buffer);
*height = buffer[3];
*height <<= 8;
*height += buffer[4];
*width = buffer[5];
*width <<= 8;
*width += buffer[6];
#if 0 // In case we need this some time
int numColorComponents = buffer[7];
int bitsPerChannel = buffer[2];
Qul::PlatformInterface::log("bpp %d\n", bitsPerChannel * numColorComponents);
#endif
*actualPixelFormat = Qul::PixelFormat_RGB32;
return true;
}
unsigned char buffer[2];
if (callback.readData(buffer, offset, sizeof(buffer)) != sizeof(buffer))
return false;
offset += sizeof(buffer);
uint32_t len = buffer[0];
len <<= 8;
len += buffer[1];
len -= 2; // The two bytes from the length itself need to be subtracted
offset += len; /* Discard data */
}
Qul::PlatformInterface::log("insufficient data for detecting image dimensions\n");
return false;
}
int DesktopImageDecoder::decodeImage(RequestDataCallback &callback,
unsigned char *outbuffer,
uint32_t outbufferSize,
Qul::PixelFormat pixelFormat,
uint32_t requiredBytesPerLine)
{
QBuffer qBuffer;
if (callback.rawData()) {
qBuffer.setData((const char *) callback.rawData(), callback.totalAvailableDataSize());
} else {
QByteArray data;
data.fill('\0', callback.totalAvailableDataSize());
if (callback.readData((unsigned char *) (data.data()), 0, data.capacity())
!= callback.totalAvailableDataSize()) {
Qul::PlatformInterface::log("Insufficient data for read\n");
return -1;
}
qBuffer.setData(data);
}
qBuffer.open(QBuffer::ReadOnly);
QImageReader imageReader(&qBuffer);
QImage image;
if (!imageReader.read(&image)) {
Qul::PlatformInterface::log(imageReader.errorString().toLocal8Bit().constData());
return -1;
}
QImage::Format imageFormat = convertColorFormat(pixelFormat);
if (imageFormat == QImage::Format_Invalid) {
Qul::PlatformInterface::log("Unsupported image format\n");
return -1;
}
image.convertTo(imageFormat);
if (image.bytesPerLine() != requiredBytesPerLine) {
Qul::PlatformInterface::log("Resulting scanline length is not expected and conversion is not implemented.\n");
return -1;
}
const uint32_t datasize = image.size().height() * image.bytesPerLine();
if (datasize > outbufferSize) {
Qul::PlatformInterface::log("Outputbuffer has insufficient size of %d, required %d\n", outbufferSize, datasize);
return -1;
}
memcpy(outbuffer, image.constBits(), datasize);
return 0;
}