29typedef quint16 ushort;
35uchar targaMagic[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
41 TGA_TYPE_RLE_INDEXED = 9,
42 TGA_TYPE_RLE_RGB = 10,
43 TGA_TYPE_RLE_GREY = 11,
46#define TGA_INTERLEAVE_MASK 0xc0
47#define TGA_INTERLEAVE_NONE 0x00
48#define TGA_INTERLEAVE_2WAY 0x40
49#define TGA_INTERLEAVE_4WAY 0x80
51#define TGA_ORIGIN_MASK 0x30
52#define TGA_ORIGIN_LEFT 0x00
53#define TGA_ORIGIN_RIGHT 0x10
54#define TGA_ORIGIN_LOWER 0x00
55#define TGA_ORIGIN_UPPER 0x20
60 uchar colormap_type = 0;
62 ushort colormap_index = 0;
63 ushort colormap_length = 0;
64 uchar colormap_size = 0;
80 s >> head.colormap_type;
82 s >> head.colormap_index;
83 s >> head.colormap_length;
84 s >> head.colormap_size;
94static bool IsSupported(
const TgaHeader &head)
96 if (head.image_type != TGA_TYPE_INDEXED && head.image_type != TGA_TYPE_RGB && head.image_type != TGA_TYPE_GREY && head.image_type != TGA_TYPE_RLE_INDEXED
97 && head.image_type != TGA_TYPE_RLE_RGB && head.image_type != TGA_TYPE_RLE_GREY) {
100 if (head.image_type == TGA_TYPE_INDEXED || head.image_type == TGA_TYPE_RLE_INDEXED) {
101 if (head.colormap_length > 256 || head.colormap_size != 24 || head.colormap_type != 1) {
105 if (head.image_type == TGA_TYPE_RGB || head.image_type == TGA_TYPE_GREY || head.image_type == TGA_TYPE_RLE_RGB || head.image_type == TGA_TYPE_RLE_GREY) {
106 if (head.colormap_type != 0) {
110 if (head.width == 0 || head.height == 0) {
113 if (head.pixel_size != 8 && head.pixel_size != 16 && head.pixel_size != 24 && head.pixel_size != 32) {
117 if (head.colormap_type == 0 && (head.colormap_size != 0 || head.colormap_index != 0 || head.colormap_length != 0)) {
129struct TgaHeaderInfo {
135 TgaHeaderInfo(
const TgaHeader &tga)
141 switch (tga.image_type) {
142 case TGA_TYPE_RLE_INDEXED:
146 case TGA_TYPE_INDEXED:
150 case TGA_TYPE_RLE_RGB:
158 case TGA_TYPE_RLE_GREY:
176 if (IsSupported(head)) {
178 const int numAlphaBits = head.flags & 0xf;
180 if ((head.pixel_size == 32) && (head.flags & 0xf)) {
181 if (numAlphaBits <= 8) {
196static bool peekHeader(
QIODevice *device, TgaHeader &header)
198 auto head = device->
peek(TgaHeader::SIZE);
199 if (head.size() < TgaHeader::SIZE) {
210 img = imageAlloc(tga.width, tga.height, imageFormat(tga));
212 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(tga.width, tga.height);
216 TgaHeaderInfo info(tga);
218 const int numAlphaBits = tga.flags & 0xf;
219 uint pixel_size = (tga.pixel_size / 8);
220 qint64 size = qint64(tga.width) * qint64(tga.height) * pixel_size;
228 static const int max_palette_size = 768;
229 char palette[max_palette_size];
232 const int palette_size = 3 * tga.colormap_length;
233 if (palette_size > max_palette_size) {
236 const int dataRead = s.
readRawData(palette, palette_size);
240 if (dataRead < max_palette_size) {
241 memset(&palette[dataRead], 0, max_palette_size - dataRead);
246 uchar *
const image =
reinterpret_cast<uchar *
>(malloc(size));
255 char *dst = (
char *)image;
256 char *imgEnd = dst + size;
259 while (num > 0 && valid) {
269 uint count = (c & 0x7f) + 1;
270 num -= count * pixel_size;
278 assert(pixel_size <= 8);
280 const int dataRead = s.
readRawData(pixel, pixel_size);
281 if (dataRead < (
int)pixel_size) {
282 memset(&pixel[dataRead], 0, pixel_size - dataRead);
285 if (dst + pixel_size > imgEnd) {
286 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(dst) << (ptrdiff_t(imgEnd) - ptrdiff_t(pixel_size));
291 memcpy(dst, pixel, pixel_size);
303 if ((uint)dataRead < count) {
304 const size_t toCopy = count - dataRead;
305 if (&dst[dataRead] + toCopy > imgEnd) {
306 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(image) << ptrdiff_t(&dst[dataRead]);
312 memset(&dst[dataRead], 0, toCopy);
319 const int dataRead = s.
readRawData((
char *)image, size);
324 if (dataRead < size) {
325 memset(&image[dataRead], 0, size - dataRead);
338 if (tga.flags & TGA_ORIGIN_UPPER) {
343 y_start = tga.height - 1;
350 for (
int y = y_start; y != y_end; y += y_step) {
351 QRgb *scanline = (QRgb *)img.
scanLine(y);
355 for (
int x = 0; x < tga.width; x++) {
357 scanline[x] = qRgb(palette[3 * idx + 2], palette[3 * idx + 1], palette[3 * idx + 0]);
359 }
else if (info.grey) {
361 for (
int x = 0; x < tga.width; x++) {
362 scanline[x] = qRgb(*src, *src, *src);
367 if (tga.pixel_size == 16) {
368 for (
int x = 0; x < tga.width; x++) {
369 Color555 c = *
reinterpret_cast<Color555 *
>(src);
370 scanline[x] = qRgb((c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2));
373 }
else if (tga.pixel_size == 24) {
374 for (
int x = 0; x < tga.width; x++) {
375 scanline[x] = qRgb(src[2], src[1], src[0]);
378 }
else if (tga.pixel_size == 32) {
379 for (
int x = 0; x < tga.width; x++) {
381 const uchar alpha = (src[3] << (8 - numAlphaBits));
382 scanline[x] = qRgba(src[2], src[1], src[0], alpha);
397class TGAHandlerPrivate
400 TGAHandlerPrivate() {}
401 ~TGAHandlerPrivate() {}
406TGAHandler::TGAHandler()
408 , d(new TGAHandlerPrivate)
412bool TGAHandler::canRead()
const
414 if (canRead(device())) {
421bool TGAHandler::read(
QImage *outImage)
426 auto&& tga = d->m_header;
427 if (!peekHeader(dev, tga) || !IsSupported(tga)) {
432 if (dev->isSequential()) {
433 dev->read(TgaHeader::SIZE + tga.id_length);
435 dev->seek(TgaHeader::SIZE + tga.id_length);
448 bool result = LoadTGA(s, tga, img);
450 if (result ==
false) {
459bool TGAHandler::write(
const QImage &image)
472 qDebug() <<
"TGAHandler::write: image conversion to 32 bits failed!";
475 static constexpr quint8 originTopLeft = TGA_ORIGIN_UPPER + TGA_ORIGIN_LEFT;
476 static constexpr quint8 alphaChannel8Bits = 0x08;
478 for (
int i = 0; i < 12; i++) {
483 s << quint16(img.
width());
484 s << quint16(img.
height());
485 s << quint8(hasAlpha ? 32 : 24);
486 s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft);
488 for (
int y = 0; y < img.
height(); y++) {
489 auto ptr =
reinterpret_cast<QRgb *
>(img.
scanLine(y));
490 for (
int x = 0; x < img.
width(); x++) {
491 auto color = *(ptr + x);
492 s << quint8(qBlue(color));
493 s << quint8(qGreen(color));
494 s << quint8(qRed(color));
496 s << quint8(qAlpha(color));
504bool TGAHandler::supportsOption(ImageOption option)
const
515QVariant TGAHandler::option(ImageOption option)
const
520 auto&& header = d->m_header;
521 if (IsSupported(header)) {
523 }
else if (
auto dev = device()) {
524 if (peekHeader(dev, header) && IsSupported(header)) {
531 auto&& header = d->m_header;
532 if (IsSupported(header)) {
534 }
else if (
auto dev = device()) {
535 if (peekHeader(dev, header) && IsSupported(header)) {
544bool TGAHandler::canRead(
QIODevice *device)
547 qWarning(
"TGAHandler::canRead() called with no device");
552 if (!peekHeader(device, tga)) {
553 qWarning(
"TGAHandler::canRead() error while reading the header");
557 return IsSupported(tga);
562 if (format ==
"tga") {
573 if (device->
isReadable() && TGAHandler::canRead(device)) {
590#include "moc_tga_p.cpp"
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
QFlags< Capability > Capabilities
bool isEmpty() const const
int readRawData(char *s, int len)
void setByteOrder(ByteOrder bo)
bool hasAlphaChannel() const const
bool isNull() const const
void setDevice(QIODevice *device)
bool isOpen() const const
bool isReadable() const const
bool isWritable() const const
QByteArray peek(qint64 maxSize)
QVariant fromValue(T &&value)