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)) {
177 TgaHeaderInfo info(head);
180 const int numAlphaBits = head.flags & 0xf;
182 if ((head.pixel_size == 32) && (numAlphaBits)) {
183 if (numAlphaBits <= 8) {
187 }
else if((info.grey) && (head.pixel_size == 16) && (numAlphaBits)) {
188 if (numAlphaBits == 8) {
202static bool peekHeader(
QIODevice *device, TgaHeader &header)
204 auto head = device->
peek(TgaHeader::SIZE);
205 if (head.size() < TgaHeader::SIZE) {
216 img = imageAlloc(tga.width, tga.height, imageFormat(tga));
218 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(tga.width, tga.height);
222 TgaHeaderInfo info(tga);
224 const int numAlphaBits = tga.flags & 0xf;
225 uint pixel_size = (tga.pixel_size / 8);
226 qint64 size = qint64(tga.width) * qint64(tga.height) * pixel_size;
234 static const int max_palette_size = 768;
235 char palette[max_palette_size];
238 const int palette_size = 3 * tga.colormap_length;
239 if (palette_size > max_palette_size) {
242 const int dataRead = s.
readRawData(palette, palette_size);
246 if (dataRead < max_palette_size) {
247 memset(&palette[dataRead], 0, max_palette_size - dataRead);
252 uchar *
const image =
reinterpret_cast<uchar *
>(malloc(size));
261 char *dst = (
char *)image;
262 char *imgEnd = dst + size;
265 while (num > 0 && valid) {
275 uint count = (c & 0x7f) + 1;
276 num -= count * pixel_size;
284 assert(pixel_size <= 8);
286 const int dataRead = s.
readRawData(pixel, pixel_size);
287 if (dataRead < (
int)pixel_size) {
288 memset(&pixel[dataRead], 0, pixel_size - dataRead);
291 if (dst + pixel_size > imgEnd) {
292 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(dst) << (ptrdiff_t(imgEnd) - ptrdiff_t(pixel_size));
297 memcpy(dst, pixel, pixel_size);
309 if ((uint)dataRead < count) {
310 const size_t toCopy = count - dataRead;
311 if (&dst[dataRead] + toCopy > imgEnd) {
312 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(image) << ptrdiff_t(&dst[dataRead]);
318 memset(&dst[dataRead], 0, toCopy);
325 const int dataRead = s.
readRawData((
char *)image, size);
330 if (dataRead < size) {
331 memset(&image[dataRead], 0, size - dataRead);
344 if (tga.flags & TGA_ORIGIN_UPPER) {
349 y_start = tga.height - 1;
356 for (
int y = y_start; y != y_end; y += y_step) {
357 auto scanline =
reinterpret_cast<QRgb *
>(img.
scanLine(y));
360 for (
int x = 0; x < tga.width; x++) {
362 scanline[x] = qRgb(palette[3 * idx + 2], palette[3 * idx + 1], palette[3 * idx + 0]);
364 }
else if (info.grey) {
366 for (
int x = 0; x < tga.width; x++) {
367 if (tga.pixel_size == 16) {
368 scanline[x] = qRgba(*src, *src, *src, *(src + 1));
372 scanline[x] = qRgb(*src, *src, *src);
378 if (tga.pixel_size == 16) {
379 for (
int x = 0; x < tga.width; x++) {
380 Color555 c = *
reinterpret_cast<Color555 *
>(src);
381 scanline[x] = qRgb((c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2));
384 }
else if (tga.pixel_size == 24) {
385 for (
int x = 0; x < tga.width; x++) {
386 scanline[x] = qRgb(src[2], src[1], src[0]);
389 }
else if (tga.pixel_size == 32) {
390 for (
int x = 0; x < tga.width; x++) {
392 const uchar alpha = (src[3] << (8 - numAlphaBits));
393 scanline[x] = qRgba(src[2], src[1], src[0], alpha);
408class TGAHandlerPrivate
411 TGAHandlerPrivate() {}
412 ~TGAHandlerPrivate() {}
417TGAHandler::TGAHandler()
419 , d(new TGAHandlerPrivate)
423bool TGAHandler::canRead()
const
425 if (canRead(device())) {
432bool TGAHandler::read(
QImage *outImage)
437 auto&& tga = d->m_header;
438 if (!peekHeader(dev, tga) || !IsSupported(tga)) {
443 if (dev->isSequential()) {
444 dev->read(TgaHeader::SIZE + tga.id_length);
446 dev->seek(TgaHeader::SIZE + tga.id_length);
459 bool result = LoadTGA(s, tga, img);
461 if (result ==
false) {
470bool TGAHandler::write(
const QImage &image)
483 qDebug() <<
"TGAHandler::write: image conversion to 32 bits failed!";
486 static constexpr quint8 originTopLeft = TGA_ORIGIN_UPPER + TGA_ORIGIN_LEFT;
487 static constexpr quint8 alphaChannel8Bits = 0x08;
489 for (
int i = 0; i < 12; i++) {
494 s << quint16(img.
width());
495 s << quint16(img.
height());
496 s << quint8(hasAlpha ? 32 : 24);
497 s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft);
499 for (
int y = 0; y < img.
height(); y++) {
500 auto ptr =
reinterpret_cast<QRgb *
>(img.
scanLine(y));
501 for (
int x = 0; x < img.
width(); x++) {
502 auto color = *(ptr + x);
503 s << quint8(qBlue(color));
504 s << quint8(qGreen(color));
505 s << quint8(qRed(color));
507 s << quint8(qAlpha(color));
515bool TGAHandler::supportsOption(ImageOption option)
const
526QVariant TGAHandler::option(ImageOption option)
const
531 auto&& header = d->m_header;
532 if (IsSupported(header)) {
534 }
else if (
auto dev = device()) {
535 if (peekHeader(dev, header) && IsSupported(header)) {
542 auto&& header = d->m_header;
543 if (IsSupported(header)) {
545 }
else if (
auto dev = device()) {
546 if (peekHeader(dev, header) && IsSupported(header)) {
555bool TGAHandler::canRead(
QIODevice *device)
558 qWarning(
"TGAHandler::canRead() called with no device");
563 if (!peekHeader(device, tga)) {
564 qWarning(
"TGAHandler::canRead() error while reading the header");
568 return IsSupported(tga);
573 if (format ==
"tga") {
584 if (device->
isReadable() && TGAHandler::canRead(device)) {
601#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)