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
62 ushort colormap_index;
63 ushort colormap_length;
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 qint64 oldPos = device->
pos();
200 int readBytes = head.
size();
203 for (
int pos = readBytes - 1; pos >= 0; --pos) {
207 device->
seek(oldPos);
210 if (readBytes < TgaHeader::SIZE) {
223 img = imageAlloc(tga.width, tga.height, imageFormat(tga));
225 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(tga.width, tga.height);
229 TgaHeaderInfo info(tga);
231 const int numAlphaBits = tga.flags & 0xf;
232 uint pixel_size = (tga.pixel_size / 8);
233 qint64 size = qint64(tga.width) * qint64(tga.height) * pixel_size;
241 static const int max_palette_size = 768;
242 char palette[max_palette_size];
245 const int palette_size = 3 * tga.colormap_length;
246 if (palette_size > max_palette_size) {
249 const int dataRead = s.
readRawData(palette, palette_size);
253 if (dataRead < max_palette_size) {
254 memset(&palette[dataRead], 0, max_palette_size - dataRead);
259 uchar *
const image =
reinterpret_cast<uchar *
>(malloc(size));
268 char *dst = (
char *)image;
269 char *imgEnd = dst + size;
272 while (num > 0 && valid) {
282 uint count = (c & 0x7f) + 1;
283 num -= count * pixel_size;
291 assert(pixel_size <= 8);
293 const int dataRead = s.
readRawData(pixel, pixel_size);
294 if (dataRead < (
int)pixel_size) {
295 memset(&pixel[dataRead], 0, pixel_size - dataRead);
298 if (dst + pixel_size > imgEnd) {
299 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(dst) << (ptrdiff_t(imgEnd) - ptrdiff_t(pixel_size));
304 memcpy(dst, pixel, pixel_size);
316 if ((uint)dataRead < count) {
317 const size_t toCopy = count - dataRead;
318 if (&dst[dataRead] + toCopy > imgEnd) {
319 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(image) << ptrdiff_t(&dst[dataRead]);
325 memset(&dst[dataRead], 0, toCopy);
332 const int dataRead = s.
readRawData((
char *)image, size);
337 if (dataRead < size) {
338 memset(&image[dataRead], 0, size - dataRead);
351 if (tga.flags & TGA_ORIGIN_UPPER) {
356 y_start = tga.height - 1;
363 for (
int y = y_start; y != y_end; y += y_step) {
364 QRgb *scanline = (QRgb *)img.
scanLine(y);
368 for (
int x = 0; x < tga.width; x++) {
370 scanline[x] = qRgb(palette[3 * idx + 2], palette[3 * idx + 1], palette[3 * idx + 0]);
372 }
else if (info.grey) {
374 for (
int x = 0; x < tga.width; x++) {
375 scanline[x] = qRgb(*src, *src, *src);
380 if (tga.pixel_size == 16) {
381 for (
int x = 0; x < tga.width; x++) {
382 Color555 c = *
reinterpret_cast<Color555 *
>(src);
383 scanline[x] = qRgb((c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2));
386 }
else if (tga.pixel_size == 24) {
387 for (
int x = 0; x < tga.width; x++) {
388 scanline[x] = qRgb(src[2], src[1], src[0]);
391 }
else if (tga.pixel_size == 32) {
392 for (
int x = 0; x < tga.width; x++) {
394 const uchar alpha = (src[3] << (8 - numAlphaBits));
395 scanline[x] = qRgba(src[2], src[1], src[0], alpha);
410TGAHandler::TGAHandler()
414bool TGAHandler::canRead()
const
416 if (canRead(device())) {
423bool TGAHandler::read(
QImage *outImage)
429 if (!peekHeader(d, tga) || !IsSupported(tga)) {
434 if (d->isSequential()) {
435 d->read(TgaHeader::SIZE + tga.id_length);
437 d->seek(TgaHeader::SIZE + tga.id_length);
450 bool result = LoadTGA(s, tga, img);
452 if (result ==
false) {
461bool TGAHandler::write(
const QImage &image)
474 qDebug() <<
"TGAHandler::write: image conversion to 32 bits failed!";
477 static constexpr quint8 originTopLeft = TGA_ORIGIN_UPPER + TGA_ORIGIN_LEFT;
478 static constexpr quint8 alphaChannel8Bits = 0x08;
480 for (
int i = 0; i < 12; i++) {
485 s << quint16(img.
width());
486 s << quint16(img.
height());
487 s << quint8(hasAlpha ? 32 : 24);
488 s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft);
490 for (
int y = 0; y < img.
height(); y++) {
491 auto ptr =
reinterpret_cast<QRgb *
>(img.
scanLine(y));
492 for (
int x = 0; x < img.
width(); x++) {
493 auto color = *(ptr + x);
494 s << quint8(qBlue(color));
495 s << quint8(qGreen(color));
496 s << quint8(qRed(color));
498 s << quint8(qAlpha(color));
506bool TGAHandler::supportsOption(ImageOption option)
const
517QVariant TGAHandler::option(ImageOption option)
const
522 if (
auto d = device()) {
524 if (peekHeader(d, header) && IsSupported(header)) {
531 if (
auto d = device()) {
533 if (peekHeader(d, header) && IsSupported(header)) {
542bool TGAHandler::canRead(
QIODevice *device)
545 qWarning(
"TGAHandler::canRead() called with no device");
549 qint64 oldPos = device->
pos();
551 int readBytes = head.
size();
554 for (
int pos = readBytes - 1; pos >= 0; --pos) {
558 device->
seek(oldPos);
561 if (readBytes < TgaHeader::SIZE) {
566 if (!peekHeader(device, tga)) {
567 qWarning(
"TGAHandler::canRead() error while reading the header");
571 return IsSupported(tga);
576 if (format ==
"tga") {
587 if (device->
isReadable() && TGAHandler::canRead(device)) {
604#include "moc_tga_p.cpp"
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
QFlags< Capability > Capabilities
bool isEmpty() const const
qsizetype size() 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
virtual bool isSequential() const const
bool isWritable() const const
virtual qint64 pos() const const
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
QVariant fromValue(T &&value)