TGA: add indexed write support

Indexed images are saved as uncompressed TGA with color map.

Closes #30
This commit is contained in:
Mirco Miranda
2025-07-03 07:36:33 +02:00
parent 4f2f2425d3
commit f6c718a789
3 changed files with 66 additions and 1 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -99,7 +99,8 @@ static bool IsSupported(const TgaHeader &head)
return false; return false;
} }
if (head.image_type == TGA_TYPE_INDEXED || head.image_type == TGA_TYPE_RLE_INDEXED) { if (head.image_type == TGA_TYPE_INDEXED || head.image_type == TGA_TYPE_RLE_INDEXED) {
if (head.colormap_length > 256 || head.colormap_type != 1) { // GIMP saves TGAs with palette size of 257 (but 256 used) so, I need to check the pixel size only.
if (head.pixel_size > 8 || head.colormap_type != 1) {
return false; return false;
} }
// colormap_size == 16 would be ARRRRRGG GGGBBBBB but we don't support that. // colormap_size == 16 would be ARRRRRGG GGGBBBBB but we don't support that.
@ -500,6 +501,59 @@ bool TGAHandler::read(QImage *outImage)
} }
bool TGAHandler::write(const QImage &image) bool TGAHandler::write(const QImage &image)
{
if (image.format() == QImage::Format_Indexed8)
return writeIndexed(image);
return writeRGBA(image);
}
bool TGAHandler::writeIndexed(const QImage &image)
{
QDataStream s(device());
s.setByteOrder(QDataStream::LittleEndian);
QImage img(image);
auto ct = img.colorTable();
s << quint8(0); // ID Length
s << quint8(1); // Color Map Type
s << quint8(TGA_TYPE_INDEXED); // Image Type
s << quint16(0); // First Entry Index
s << quint16(ct.size()); // Color Map Length
s << quint8(32); // Color map Entry Size
s << quint16(0); // X-origin of Image
s << quint16(0); // Y-origin of Image
s << quint16(img.width()); // Image Width
s << quint16(img.height()); // Image Height
s << quint8(8); // Pixe Depth
s << quint8(TGA_ORIGIN_UPPER + TGA_ORIGIN_LEFT); // Image Descriptor
for (auto &&rgb : ct) {
s << quint8(qBlue(rgb));
s << quint8(qGreen(rgb));
s << quint8(qRed(rgb));
s << quint8(qAlpha(rgb));
}
if (s.status() != QDataStream::Ok) {
return false;
}
for (int y = 0; y < img.height(); y++) {
auto ptr = img.constScanLine(y);
for (int x = 0; x < img.width(); x++) {
s << *(ptr + x);
}
if (s.status() != QDataStream::Ok) {
return false;
}
}
return true;
}
bool TGAHandler::writeRGBA(const QImage &image)
{ {
QDataStream s(device()); QDataStream s(device());
s.setByteOrder(QDataStream::LittleEndian); s.setByteOrder(QDataStream::LittleEndian);
@ -535,6 +589,10 @@ bool TGAHandler::write(const QImage &image)
s << quint8(hasAlpha ? 32 : 24); // depth (24 bit RGB + 8 bit alpha) s << quint8(hasAlpha ? 32 : 24); // depth (24 bit RGB + 8 bit alpha)
s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft); // top left image (0x20) + 8 bit alpha (0x8) s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft); // top left image (0x20) + 8 bit alpha (0x8)
if (s.status() != QDataStream::Ok) {
return false;
}
for (int y = 0; y < img.height(); y++) { for (int y = 0; y < img.height(); y++) {
auto ptr = reinterpret_cast<const QRgb *>(img.constScanLine(y)); auto ptr = reinterpret_cast<const QRgb *>(img.constScanLine(y));
for (int x = 0; x < img.width(); x++) { for (int x = 0; x < img.width(); x++) {
@ -546,6 +604,9 @@ bool TGAHandler::write(const QImage &image)
s << quint8(qAlpha(color)); s << quint8(qAlpha(color));
} }
} }
if (s.status() != QDataStream::Ok) {
return false;
}
} }
return true; return true;

View File

@ -27,6 +27,10 @@ public:
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private: private:
bool writeIndexed(const QImage &image);
bool writeRGBA(const QImage &image);
const QScopedPointer<TGAHandlerPrivate> d; const QScopedPointer<TGAHandlerPrivate> d;
}; };