TGA: added options support

Code aligned with KF6 (MR !210) to mitigate CCBUG: 413801 and CCBUG: 479612

- Added Size and Format options support
- Fixed a double image allocation when reading RGBA images (RGB was always allocated and then replaced by RGBA one)
- Fixed the code for sequential devices
This commit is contained in:
Mirco Miranda 2024-03-14 21:46:04 +00:00 committed by Albert Astals Cid
parent 7cc4cb8d0c
commit f5962442ca
2 changed files with 107 additions and 35 deletions

View File

@ -88,10 +88,6 @@ static QDataStream &operator>>(QDataStream &s, TgaHeader &head)
s >> head.height;
s >> head.pixel_size;
s >> head.flags;
/*qDebug() << "id_length: " << head.id_length << " - colormap_type: " << head.colormap_type << " - image_type: " << head.image_type;
qDebug() << "colormap_index: " << head.colormap_index << " - colormap_length: " << head.colormap_length << " - colormap_size: " << head.colormap_size;
qDebug() << "x_origin: " << head.x_origin << " - y_origin: " << head.y_origin << " - width:" << head.width << " - height:" << head.height << " - pixelsize:
" << head.pixel_size << " - flags: " << head.flags;*/
return s;
}
@ -174,10 +170,57 @@ struct TgaHeaderInfo {
}
};
static QImage::Format imageFormat(const TgaHeader &head)
{
auto format = QImage::Format_Invalid;
if (IsSupported(head)) {
// Bits 0-3 are the numbers of alpha bits (can be zero!)
const int numAlphaBits = head.flags & 0xf;
// However alpha exists only in the 32 bit format.
if ((head.pixel_size == 32) && (head.flags & 0xf)) {
if (numAlphaBits <= 8) {
format = QImage::Format_ARGB32;
}
}
else {
format = QImage::Format_RGB32;
}
}
return format;
}
/*!
* \brief peekHeader
* Reads the header but does not change the position in the device.
*/
static bool peekHeader(QIODevice *device, TgaHeader &header)
{
qint64 oldPos = device->pos();
QByteArray head = device->read(TgaHeader::SIZE);
int readBytes = head.size();
if (device->isSequential()) {
for (int pos = readBytes - 1; pos >= 0; --pos) {
device->ungetChar(head[pos]);
}
} else {
device->seek(oldPos);
}
if (readBytes < TgaHeader::SIZE) {
return false;
}
QDataStream stream(head);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> header;
return true;
}
static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img)
{
// Create image.
img = imageAlloc(tga.width, tga.height, QImage::Format_RGB32);
img = imageAlloc(tga.width, tga.height, imageFormat(tga));
if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height);
return false;
@ -185,21 +228,7 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img)
TgaHeaderInfo info(tga);
// Bits 0-3 are the numbers of alpha bits (can be zero!)
const int numAlphaBits = tga.flags & 0xf;
// However alpha exists only in the 32 bit format.
if ((tga.pixel_size == 32) && (tga.flags & 0xf)) {
img = imageAlloc(tga.width, tga.height, QImage::Format_ARGB32);
if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(tga.width, tga.height);
return false;
}
if (numAlphaBits > 8) {
return false;
}
}
uint pixel_size = (tga.pixel_size / 8);
qint64 size = qint64(tga.width) * qint64(tga.height) * pixel_size;
@ -395,23 +424,25 @@ bool TGAHandler::read(QImage *outImage)
{
// qDebug() << "Loading TGA file!";
QDataStream s(device());
s.setByteOrder(QDataStream::LittleEndian);
// Read image header.
auto d = device();
TgaHeader tga;
s >> tga;
s.device()->seek(TgaHeader::SIZE + tga.id_length);
// Check image file format.
if (s.atEnd()) {
if (!peekHeader(d, tga) || !IsSupported(tga)) {
// qDebug() << "This TGA file is not valid.";
return false;
}
// Check supported file types.
if (!IsSupported(tga)) {
// qDebug() << "This TGA file is not supported.";
if (d->isSequential()) {
d->read(TgaHeader::SIZE + tga.id_length);
} else {
d->seek(TgaHeader::SIZE + tga.id_length);
}
QDataStream s(d);
s.setByteOrder(QDataStream::LittleEndian);
// Check image file format.
if (s.atEnd()) {
// qDebug() << "This TGA file is not valid.";
return false;
}
@ -472,6 +503,42 @@ bool TGAHandler::write(const QImage &image)
return true;
}
bool TGAHandler::supportsOption(ImageOption option) const
{
if (option == QImageIOHandler::Size) {
return true;
}
if (option == QImageIOHandler::ImageFormat) {
return true;
}
return false;
}
QVariant TGAHandler::option(ImageOption option) const
{
QVariant v;
if (option == QImageIOHandler::Size) {
if (auto d = device()) {
TgaHeader header;
if (peekHeader(d, header) && IsSupported(header)) {
v = QVariant::fromValue(QSize(header.width, header.height));
}
}
}
if (option == QImageIOHandler::ImageFormat) {
if (auto d = device()) {
TgaHeader header;
if (peekHeader(d, header) && IsSupported(header)) {
v = QVariant::fromValue(imageFormat(header));
}
}
}
return v;
}
bool TGAHandler::canRead(QIODevice *device)
{
if (!device) {
@ -495,10 +562,12 @@ bool TGAHandler::canRead(QIODevice *device)
return false;
}
QDataStream stream(head);
stream.setByteOrder(QDataStream::LittleEndian);
TgaHeader tga;
stream >> tga;
if (!peekHeader(device, tga)) {
qWarning("TGAHandler::canRead() error while reading the header");
return false;
}
return IsSupported(tga);
}

View File

@ -19,6 +19,9 @@ public:
bool read(QImage *image) override;
bool write(const QImage &image) override;
bool supportsOption(QImageIOHandler::ImageOption option) const override;
QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device);
};