30typedef quint16 ushort;
36uchar targaMagic[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
42 TGA_TYPE_RLE_INDEXED = 9,
43 TGA_TYPE_RLE_RGB = 10,
44 TGA_TYPE_RLE_GREY = 11,
47#define TGA_INTERLEAVE_MASK 0xc0
48#define TGA_INTERLEAVE_NONE 0x00
49#define TGA_INTERLEAVE_2WAY 0x40
50#define TGA_INTERLEAVE_4WAY 0x80
52#define TGA_ORIGIN_MASK 0x30
53#define TGA_ORIGIN_LEFT 0x00
54#define TGA_ORIGIN_RIGHT 0x10
55#define TGA_ORIGIN_LOWER 0x00
56#define TGA_ORIGIN_UPPER 0x20
61 uchar colormap_type = 0;
63 ushort colormap_index = 0;
64 ushort colormap_length = 0;
65 uchar colormap_size = 0;
81 s >> head.colormap_type;
83 s >> head.colormap_index;
84 s >> head.colormap_length;
85 s >> head.colormap_size;
95static bool IsSupported(
const TgaHeader &head)
97 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
98 && head.image_type != TGA_TYPE_RLE_RGB && head.image_type != TGA_TYPE_RLE_GREY) {
101 if (head.image_type == TGA_TYPE_INDEXED || head.image_type == TGA_TYPE_RLE_INDEXED) {
102 if (head.colormap_length > 256 || head.colormap_size != 24 || head.colormap_type != 1) {
106 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) {
107 if (head.colormap_type != 0) {
111 if (head.width == 0 || head.height == 0) {
114 if (head.pixel_size != 8 && head.pixel_size != 16 && head.pixel_size != 24 && head.pixel_size != 32) {
118 if (head.colormap_type == 0 && (head.colormap_size != 0 || head.colormap_index != 0 || head.colormap_length != 0)) {
130struct TgaHeaderInfo {
136 TgaHeaderInfo(
const TgaHeader &tga)
142 switch (tga.image_type) {
143 case TGA_TYPE_RLE_INDEXED:
147 case TGA_TYPE_INDEXED:
151 case TGA_TYPE_RLE_RGB:
159 case TGA_TYPE_RLE_GREY:
177 if (IsSupported(head)) {
178 TgaHeaderInfo info(head);
181 const int numAlphaBits = head.flags & 0xf;
183 if ((head.pixel_size == 32) && (numAlphaBits)) {
184 if (numAlphaBits <= 8) {
188 }
else if((info.grey) && (head.pixel_size == 16) && (numAlphaBits)) {
189 if (numAlphaBits == 8) {
203static bool peekHeader(
QIODevice *device, TgaHeader &header)
205 auto head = device->
peek(TgaHeader::SIZE);
206 if (head.size() < TgaHeader::SIZE) {
217 img = imageAlloc(tga.width, tga.height, imageFormat(tga));
219 qWarning() <<
"Failed to allocate image, invalid dimensions?" <<
QSize(tga.width, tga.height);
223 TgaHeaderInfo info(tga);
225 const int numAlphaBits = tga.flags & 0xf;
226 uint pixel_size = (tga.pixel_size / 8);
227 qint64 size = qint64(tga.width) * qint64(tga.height) * pixel_size;
235 static const int max_palette_size = 768;
236 char palette[max_palette_size];
239 const int palette_size = 3 * tga.colormap_length;
240 if (palette_size > max_palette_size) {
243 const int dataRead = s.
readRawData(palette, palette_size);
247 if (dataRead < max_palette_size) {
248 memset(&palette[dataRead], 0, max_palette_size - dataRead);
253 uchar *
const image =
reinterpret_cast<uchar *
>(malloc(size));
262 char *dst = (
char *)image;
263 char *imgEnd = dst + size;
266 while (num > 0 && valid) {
276 uint count = (c & 0x7f) + 1;
277 num -= count * pixel_size;
285 assert(pixel_size <= 8);
287 const int dataRead = s.
readRawData(pixel, pixel_size);
288 if (dataRead < (
int)pixel_size) {
289 memset(&pixel[dataRead], 0, pixel_size - dataRead);
292 if (dst + pixel_size > imgEnd) {
293 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(dst) << (ptrdiff_t(imgEnd) - ptrdiff_t(pixel_size));
298 memcpy(dst, pixel, pixel_size);
310 if ((uint)dataRead < count) {
311 const size_t toCopy = count - dataRead;
312 if (&dst[dataRead] + toCopy > imgEnd) {
313 qWarning() <<
"Trying to write out of bounds!" << ptrdiff_t(image) << ptrdiff_t(&dst[dataRead]);
319 memset(&dst[dataRead], 0, toCopy);
326 const int dataRead = s.
readRawData((
char *)image, size);
331 if (dataRead < size) {
332 memset(&image[dataRead], 0, size - dataRead);
345 if (tga.flags & TGA_ORIGIN_UPPER) {
350 y_start = tga.height - 1;
357 for (
int y = y_start; y != y_end; y += y_step) {
358 auto scanline =
reinterpret_cast<QRgb *
>(img.
scanLine(y));
361 for (
int x = 0; x < tga.width; x++) {
363 scanline[x] = qRgb(palette[3 * idx + 2], palette[3 * idx + 1], palette[3 * idx + 0]);
365 }
else if (info.grey) {
367 for (
int x = 0; x < tga.width; x++) {
368 if (tga.pixel_size == 16) {
369 scanline[x] = qRgba(*src, *src, *src, *(src + 1));
373 scanline[x] = qRgb(*src, *src, *src);
379 if (tga.pixel_size == 16) {
380 for (
int x = 0; x < tga.width; x++) {
381 Color555 c = *
reinterpret_cast<Color555 *
>(src);
382 scanline[x] = qRgb((c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2));
385 }
else if (tga.pixel_size == 24) {
386 for (
int x = 0; x < tga.width; x++) {
387 scanline[x] = qRgb(src[2], src[1], src[0]);
390 }
else if (tga.pixel_size == 32) {
391 for (
int x = 0; x < tga.width; x++) {
393 const uchar alpha = (src[3] << (8 - numAlphaBits));
394 scanline[x] = qRgba(src[2], src[1], src[0], alpha);
409class TGAHandlerPrivate
412 TGAHandlerPrivate() {}
413 ~TGAHandlerPrivate() {}
418TGAHandler::TGAHandler()
420 , d(new TGAHandlerPrivate)
424bool TGAHandler::canRead()
const
426 if (canRead(device())) {
433bool TGAHandler::read(
QImage *outImage)
438 auto&& tga = d->m_header;
439 if (!peekHeader(dev, tga) || !IsSupported(tga)) {
444 if (dev->isSequential()) {
445 dev->read(TgaHeader::SIZE + tga.id_length);
447 dev->seek(TgaHeader::SIZE + tga.id_length);
460 bool result = LoadTGA(s, tga, img);
462 if (result ==
false) {
471bool TGAHandler::write(
const QImage &image)
478#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
480 if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.
format() == QImage::Format_CMYK8888) {
491 qDebug() <<
"TGAHandler::write: image conversion to 32 bits failed!";
494 static constexpr quint8 originTopLeft = TGA_ORIGIN_UPPER + TGA_ORIGIN_LEFT;
495 static constexpr quint8 alphaChannel8Bits = 0x08;
497 for (
int i = 0; i < 12; i++) {
502 s << quint16(img.
width());
503 s << quint16(img.
height());
504 s << quint8(hasAlpha ? 32 : 24);
505 s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft);
507 for (
int y = 0; y < img.
height(); y++) {
508 auto ptr =
reinterpret_cast<const QRgb *
>(img.
constScanLine(y));
509 for (
int x = 0; x < img.
width(); x++) {
510 auto color = *(ptr + x);
511 s << quint8(qBlue(color));
512 s << quint8(qGreen(color));
513 s << quint8(qRed(color));
515 s << quint8(qAlpha(color));
523bool TGAHandler::supportsOption(ImageOption option)
const
534QVariant TGAHandler::option(ImageOption option)
const
539 auto&& header = d->m_header;
540 if (IsSupported(header)) {
542 }
else if (
auto dev = device()) {
543 if (peekHeader(dev, header) && IsSupported(header)) {
550 auto&& header = d->m_header;
551 if (IsSupported(header)) {
553 }
else if (
auto dev = device()) {
554 if (peekHeader(dev, header) && IsSupported(header)) {
563bool TGAHandler::canRead(
QIODevice *device)
566 qWarning(
"TGAHandler::canRead() called with no device");
571 if (!peekHeader(device, tga)) {
572 qWarning(
"TGAHandler::canRead() error while reading the header");
576 return IsSupported(tga);
581 if (format ==
"tga") {
592 if (device->
isReadable() && TGAHandler::canRead(device)) {
609#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)
QColorSpace colorSpace() const const
const uchar * constScanLine(int i) const const
QImage convertedToColorSpace(const QColorSpace &colorSpace) const const
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)