1/*
2 kimgio module for SGI images
3 SPDX-FileCopyrightText: 2004 Melchior FRANZ <mfranz@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8/* this code supports:
9 * reading:
10 * everything, except images with 1 dimension or images with
11 * mapmode != NORMAL (e.g. dithered); Images with 16 bit
12 * precision or more than 4 layers are stripped down.
13 * writing:
14 * Run Length Encoded (RLE) or Verbatim (uncompressed)
15 * (whichever is smaller)
16 *
17 * Please report if you come across rgb/rgba/sgi/bw files that aren't
18 * recognized. Also report applications that can't deal with images
19 * saved by this filter.
20 */
21
22#include "rgb_p.h"
23#include "scanlineconverter_p.h"
24#include "util_p.h"
25
26#include <cstring>
27
28#include <QColorSpace>
29#include <QImage>
30#include <QList>
31#include <QLoggingCategory>
32#include <QMap>
33
34#ifdef QT_DEBUG
35Q_LOGGING_CATEGORY(LOG_RGBPLUGIN, "kf.imageformats.plugins.rgb", QtDebugMsg)
36#else
37Q_LOGGING_CATEGORY(LOG_RGBPLUGIN, "kf.imageformats.plugins.rgb", QtWarningMsg)
38#endif
39
40class RLEData : public QList<uchar>
41{
42public:
43 RLEData()
44 {
45 }
46 RLEData(const uchar *d, uint l, uint o)
47 : _offset(o)
48 {
49 for (uint i = 0; i < l; i++) {
50 append(t: d[i]);
51 }
52 }
53 bool operator<(const RLEData &) const;
54 void write(QDataStream &s);
55 uint offset() const
56 {
57 return _offset;
58 }
59
60private:
61 uint _offset;
62};
63
64class RLEMap : public QMap<RLEData, uint>
65{
66public:
67 RLEMap()
68 : _counter(0)
69 , _offset(0)
70 {
71 }
72 uint insert(const uchar *d, uint l);
73 QList<const RLEData *> vector();
74 void setBaseOffset(uint o)
75 {
76 _offset = o;
77 }
78
79private:
80 uint _counter;
81 uint _offset;
82};
83
84class SGIImagePrivate
85{
86public:
87 SGIImagePrivate();
88 ~SGIImagePrivate();
89
90 bool readImage(QImage &);
91 bool writeImage(const QImage &);
92
93 bool isValid() const;
94 bool isSupported() const;
95
96 bool peekHeader(QIODevice *device);
97
98 QSize size() const;
99 QImage::Format format() const;
100
101 void setDevice(QIODevice *device);
102
103private:
104 enum {
105 NORMAL,
106 DITHERED,
107 SCREEN,
108 COLORMAP,
109 }; // colormap
110 QIODevice *_dev;
111 QDataStream _stream;
112
113 quint16 _magic = 0;
114 quint8 _rle = 0;
115 quint8 _bpc = 0;
116 quint16 _dim = 0;
117 quint16 _xsize = 0;
118 quint16 _ysize = 0;
119 quint16 _zsize = 0;
120 quint32 _pixmin = 0;
121 quint32 _pixmax = 0;
122 char _imagename[80];
123 quint32 _colormap = 0;
124 quint8 _unused[404];
125 quint32 _unused32 = 0;
126
127 quint32 *_starttab;
128 quint32 *_lengthtab;
129 QByteArray _data;
130 QByteArray::Iterator _pos;
131 RLEMap _rlemap;
132 QList<const RLEData *> _rlevector;
133 uint _numrows;
134
135 bool readData(QImage &);
136 bool getRow(uchar *dest);
137 bool readHeader();
138
139 static bool readHeader(QDataStream &ds, SGIImagePrivate *sgi);
140
141 bool writeHeader();
142 bool writeRle();
143 bool writeVerbatim(const QImage &, const QImage::Format&, const QColorSpace&);
144 bool scanData(const QImage &, const QImage::Format&, const QColorSpace&);
145 uint compact(uchar *, uchar *);
146 uchar intensity(uchar);
147};
148
149SGIImagePrivate::SGIImagePrivate()
150 : _dev(nullptr)
151 , _starttab(nullptr)
152 , _lengthtab(nullptr)
153{
154 std::memset(s: _imagename, c: 0, n: sizeof(_imagename));
155 std::memset(s: _unused, c: 0, n: sizeof(_unused));
156}
157
158SGIImagePrivate::~SGIImagePrivate()
159{
160 delete[] _starttab;
161 delete[] _lengthtab;
162}
163
164///////////////////////////////////////////////////////////////////////////////
165
166void SGIImagePrivate::setDevice(QIODevice *device)
167{
168 _dev = device;
169 _stream.setDevice(_dev);
170}
171
172bool SGIImagePrivate::getRow(uchar *dest)
173{
174 int n;
175 int i;
176 if (!_rle) {
177 for (i = 0; i < _xsize; i++) {
178 if (_pos >= _data.end()) {
179 return false;
180 }
181 dest[i] = uchar(*_pos);
182 _pos += _bpc;
183 }
184 return true;
185 }
186
187 for (i = 0; i < _xsize;) {
188 if (_bpc == 2) {
189 _pos++;
190 }
191 if (_pos >= _data.end()) {
192 return false;
193 }
194 n = *_pos & 0x7f;
195 if (!n) {
196 break;
197 }
198
199 if (*_pos++ & 0x80) {
200 for (; i < _xsize && _pos < _data.end() && n--; i++) {
201 *dest++ = *_pos;
202 _pos += _bpc;
203 }
204 } else {
205 for (; i < _xsize && n--; i++) {
206 *dest++ = *_pos;
207 }
208
209 _pos += _bpc;
210 }
211 }
212 return i == _xsize;
213}
214
215bool SGIImagePrivate::readData(QImage &img)
216{
217 QRgb *c;
218 quint32 *start = _starttab;
219 QByteArray lguard(_xsize, 0);
220 uchar *line = (uchar *)lguard.data();
221 unsigned x;
222 unsigned y;
223
224 if (!_rle) {
225 _pos = _data.begin();
226 }
227
228 for (y = 0; y < _ysize; y++) {
229 if (_rle) {
230 _pos = _data.begin() + *start++;
231 }
232 if (!getRow(dest: line)) {
233 return false;
234 }
235 c = reinterpret_cast<QRgb *>(img.scanLine(_ysize - y - 1));
236 for (x = 0; x < _xsize; x++, c++) {
237 *c = qRgb(r: line[x], g: line[x], b: line[x]);
238 }
239 }
240
241 if (_zsize == 1) {
242 return true;
243 }
244
245 if (_zsize != 2) {
246 for (y = 0; y < _ysize; y++) {
247 if (_rle) {
248 _pos = _data.begin() + *start++;
249 }
250 if (!getRow(dest: line)) {
251 return false;
252 }
253 c = reinterpret_cast<QRgb *>(img.scanLine(_ysize - y - 1));
254 for (x = 0; x < _xsize; x++, c++) {
255 *c = qRgb(r: qRed(rgb: *c), g: line[x], b: line[x]);
256 }
257 }
258
259 for (y = 0; y < _ysize; y++) {
260 if (_rle) {
261 _pos = _data.begin() + *start++;
262 }
263 if (!getRow(dest: line)) {
264 return false;
265 }
266 c = reinterpret_cast<QRgb *>(img.scanLine(_ysize - y - 1));
267 for (x = 0; x < _xsize; x++, c++) {
268 *c = qRgb(r: qRed(rgb: *c), g: qGreen(rgb: *c), b: line[x]);
269 }
270 }
271
272 if (_zsize == 3) {
273 return true;
274 }
275 }
276
277 for (y = 0; y < _ysize; y++) {
278 if (_rle) {
279 _pos = _data.begin() + *start++;
280 }
281 if (!getRow(dest: line)) {
282 return false;
283 }
284 c = reinterpret_cast<QRgb *>(img.scanLine(_ysize - y - 1));
285 for (x = 0; x < _xsize; x++, c++) {
286 *c = qRgba(r: qRed(rgb: *c), g: qGreen(rgb: *c), b: qBlue(rgb: *c), a: line[x]);
287 }
288 }
289
290 return true;
291}
292
293bool SGIImagePrivate::readImage(QImage &img)
294{
295 if (!readHeader() || !isSupported()) {
296 return false;
297 }
298
299 if (_stream.atEnd()) {
300 return false;
301 }
302
303 img = imageAlloc(size: size(), format: format());
304 if (img.isNull()) {
305 qCWarning(LOG_RGBPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(_xsize, _ysize);
306 return false;
307 }
308
309 if (_zsize > 4) {
310 // qCDebug(LOG_RGBPLUGIN) << "using first 4 of " << _zsize << " channels";
311 // Only let this continue if it won't cause a int overflow later
312 // this is most likely a broken file anyway
313 if (_ysize > std::numeric_limits<int>::max() / _zsize) {
314 return false;
315 }
316 }
317
318 _numrows = _ysize * _zsize;
319
320 if (_rle) {
321 uint l;
322 _starttab = new quint32[_numrows];
323 for (l = 0; !_stream.atEnd() && l < _numrows; l++) {
324 _stream >> _starttab[l];
325 _starttab[l] -= 512 + _numrows * 2 * sizeof(quint32);
326 if (_stream.status() != QDataStream::Ok) {
327 return false;
328 }
329 }
330 for (; l < _numrows; l++) {
331 _starttab[l] = 0;
332 }
333
334 _lengthtab = new quint32[_numrows];
335 for (l = 0; !_stream.atEnd() && l < _numrows; l++) {
336 _stream >> _lengthtab[l];
337 if (_stream.status() != QDataStream::Ok) {
338 return false;
339 }
340 }
341 for (; l < _numrows; l++) {
342 _lengthtab[l] = 0;
343 }
344 }
345
346 if (_stream.status() != QDataStream::Ok) {
347 return false;
348 }
349
350 _data = _dev->readAll();
351
352 // sanity check
353 if (_rle) {
354 for (uint o = 0; o < _numrows; o++) {
355 // don't change to greater-or-equal!
356 if (_starttab[o] + _lengthtab[o] > (uint)_data.size()) {
357 // qCDebug(LOG_RGBPLUGIN) << "image corrupt (sanity check failed)";
358 return false;
359 }
360 }
361 }
362
363 if (!readData(img)) {
364 // qCDebug(LOG_RGBPLUGIN) << "image corrupt (incomplete scanline)";
365 return false;
366 }
367
368 return true;
369}
370
371///////////////////////////////////////////////////////////////////////////////
372
373void RLEData::write(QDataStream &s)
374{
375 for (int i = 0; i < size(); i++) {
376 s << at(i);
377 }
378}
379
380bool RLEData::operator<(const RLEData &b) const
381{
382 uchar ac;
383 uchar bc;
384 for (int i = 0; i < qMin(a: size(), b: b.size()); i++) {
385 ac = at(i);
386 bc = b[i];
387 if (ac != bc) {
388 return ac < bc;
389 }
390 }
391 return size() < b.size();
392}
393
394uint RLEMap::insert(const uchar *d, uint l)
395{
396 RLEData data = RLEData(d, l, _offset);
397 Iterator it = find(key: data);
398 if (it != end()) {
399 return it.value();
400 }
401
402 _offset += l;
403 return QMap<RLEData, uint>::insert(key: data, value: _counter++).value();
404}
405
406QList<const RLEData *> RLEMap::vector()
407{
408 QList<const RLEData *> v(size());
409 for (Iterator it = begin(); it != end(); ++it) {
410 v.replace(i: it.value(), t: &it.key());
411 }
412
413 return v;
414}
415
416uchar SGIImagePrivate::intensity(uchar c)
417{
418 if (c < _pixmin) {
419 _pixmin = c;
420 }
421 if (c > _pixmax) {
422 _pixmax = c;
423 }
424 return c;
425}
426
427uint SGIImagePrivate::compact(uchar *d, uchar *s)
428{
429 uchar *dest = d;
430 uchar *src = s;
431 uchar patt;
432 uchar *t;
433 uchar *end = s + _xsize;
434 int i;
435 int n;
436 while (src < end) {
437 for (n = 0, t = src; t + 2 < end && !(*t == t[1] && *t == t[2]); t++) {
438 n++;
439 }
440
441 while (n) {
442 i = n > 126 ? 126 : n;
443 n -= i;
444 *dest++ = 0x80 | i;
445 while (i--) {
446 *dest++ = *src++;
447 }
448 }
449
450 if (src == end) {
451 break;
452 }
453
454 patt = *src++;
455 for (n = 1; src < end && *src == patt; src++) {
456 n++;
457 }
458
459 while (n) {
460 i = n > 126 ? 126 : n;
461 n -= i;
462 *dest++ = i;
463 *dest++ = patt;
464 }
465 }
466 *dest++ = 0;
467 return dest - d;
468}
469
470bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, const QColorSpace &tcs)
471{
472 quint32 *start = _starttab;
473 QByteArray lineguard(_xsize * 2, 0);
474 QByteArray bufguard(_xsize, 0);
475 uchar *line = (uchar *)lineguard.data();
476 uchar *buf = (uchar *)bufguard.data();
477 const QRgb *c;
478 unsigned x;
479 unsigned y;
480 uint len;
481
482 ScanLineConverter scl(tfmt);
483 scl.setTargetColorSpace(tcs);
484 for (y = 0; y < _ysize; y++) {
485 const int yPos = _ysize - y - 1; // scanline doesn't do any sanity checking
486 if (yPos >= img.height()) {
487 qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
488 return false;
489 }
490
491 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: yPos));
492
493 for (x = 0; x < _xsize; x++) {
494 buf[x] = intensity(c: qRed(rgb: *c++));
495 }
496 len = compact(d: line, s: buf);
497 *start++ = _rlemap.insert(d: line, l: len);
498 }
499
500 if (_zsize == 1) {
501 return true;
502 }
503
504 if (_zsize != 2) {
505 for (y = 0; y < _ysize; y++) {
506 const int yPos = _ysize - y - 1;
507 if (yPos >= img.height()) {
508 qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
509 return false;
510 }
511
512 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: yPos));
513 for (x = 0; x < _xsize; x++) {
514 buf[x] = intensity(c: qGreen(rgb: *c++));
515 }
516 len = compact(d: line, s: buf);
517 *start++ = _rlemap.insert(d: line, l: len);
518 }
519
520 for (y = 0; y < _ysize; y++) {
521 const int yPos = _ysize - y - 1;
522 if (yPos >= img.height()) {
523 qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
524 return false;
525 }
526
527 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: yPos));
528 for (x = 0; x < _xsize; x++) {
529 buf[x] = intensity(c: qBlue(rgb: *c++));
530 }
531 len = compact(d: line, s: buf);
532 *start++ = _rlemap.insert(d: line, l: len);
533 }
534
535 if (_zsize == 3) {
536 return true;
537 }
538 }
539
540 for (y = 0; y < _ysize; y++) {
541 const int yPos = _ysize - y - 1;
542 if (yPos >= img.height()) {
543 qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
544 return false;
545 }
546
547 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: yPos));
548 for (x = 0; x < _xsize; x++) {
549 buf[x] = intensity(c: qAlpha(rgb: *c++));
550 }
551 len = compact(d: line, s: buf);
552 *start++ = _rlemap.insert(d: line, l: len);
553 }
554
555 return true;
556}
557
558bool SGIImagePrivate::isValid() const
559{
560 // File signature/magic number
561 if (_magic != 0x01da) {
562 return false;
563 }
564 // Compression, 0 = Uncompressed, 1 = RLE compressed
565 if (_rle > 1) {
566 return false;
567 }
568 // Bytes per pixel, 1 = 8 bit, 2 = 16 bit
569 if (_bpc != 1 && _bpc != 2) {
570 return false;
571 }
572 // Image dimension, 3 for RGBA image
573 if (_dim < 1 || _dim > 3) {
574 return false;
575 }
576 // Number channels in the image file, 4 for RGBA image
577 if (_zsize < 1) {
578 return false;
579 }
580 return true;
581}
582
583bool SGIImagePrivate::isSupported() const
584{
585 if (!isValid()) {
586 return false;
587 }
588 if (_colormap != NORMAL) {
589 return false; // only NORMAL supported
590 }
591 if (_dim == 1) {
592 return false;
593 }
594 return true;
595}
596
597bool SGIImagePrivate::peekHeader(QIODevice *device)
598{
599 QDataStream ds(device->peek(maxlen: 512));
600 return SGIImagePrivate::readHeader(ds, sgi: this) && isValid();
601}
602
603QSize SGIImagePrivate::size() const
604{
605 return QSize(_xsize, _ysize);
606}
607
608QImage::Format SGIImagePrivate::format() const
609{
610 if (_zsize == 2 || _zsize == 4) {
611 return QImage::Format_ARGB32;
612 }
613 return QImage::Format_RGB32;
614}
615
616bool SGIImagePrivate::readHeader()
617{
618 return readHeader(ds&: _stream, sgi: this);
619}
620
621bool SGIImagePrivate::readHeader(QDataStream &ds, SGIImagePrivate *sgi)
622{
623 // magic
624 ds >> sgi->_magic;
625
626 // verbatim/rle
627 ds >> sgi->_rle;
628
629 // bytes per channel
630 ds >> sgi->_bpc;
631
632 // number of dimensions
633 ds >> sgi->_dim;
634
635 ds >> sgi->_xsize >> sgi->_ysize >> sgi->_zsize >> sgi->_pixmin >> sgi->_pixmax >> sgi->_unused32;
636
637 // name
638 ds.readRawData(sgi->_imagename, len: 80);
639 sgi->_imagename[79] = '\0';
640
641 ds >> sgi->_colormap;
642
643 for (size_t i = 0; i < sizeof(_unused); i++) {
644 ds >> sgi->_unused[i];
645 }
646
647 return ds.status() == QDataStream::Ok;
648}
649
650bool SGIImagePrivate::writeHeader()
651{
652 _stream << _magic;
653 _stream << _rle << _bpc << _dim;
654 _stream << _xsize << _ysize << _zsize;
655 _stream << _pixmin << _pixmax;
656 _stream << _unused32;
657
658 for (int i = 0; i < 80; i++) {
659 _imagename[i] = '\0';
660 }
661 _stream.writeRawData(_imagename, len: 80);
662
663 _stream << _colormap;
664 for (size_t i = 0; i < sizeof(_unused); i++) {
665 _stream << _unused[i];
666 }
667 return _stream.status() == QDataStream::Ok;
668}
669
670bool SGIImagePrivate::writeRle()
671{
672 _rle = 1;
673 // qCDebug(LOG_RGBPLUGIN) << "writing RLE data";
674 if (!writeHeader()) {
675 return false;
676 }
677
678 uint i;
679
680 // write start table
681 for (i = 0; i < _numrows; i++) {
682 _stream << quint32(_rlevector[_starttab[i]]->offset());
683 }
684
685 // write length table
686 for (i = 0; i < _numrows; i++) {
687 _stream << quint32(_rlevector[_starttab[i]]->size());
688 }
689
690 // write data
691 for (i = 0; (int)i < _rlevector.size(); i++) {
692 const_cast<RLEData *>(_rlevector[i])->write(s&: _stream);
693 }
694
695 return _stream.status() == QDataStream::Ok;
696}
697
698bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfmt, const QColorSpace &tcs)
699{
700 _rle = 0;
701 if (!writeHeader()) {
702 return false;
703 }
704
705 const QRgb *c;
706 unsigned x;
707 unsigned y;
708
709 ScanLineConverter scl(tfmt);
710 scl.setTargetColorSpace(tcs);
711 QByteArray ba(_xsize, char());
712 for (y = 0; y < _ysize; y++) {
713 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: _ysize - y - 1));
714 for (x = 0; x < _xsize; x++) {
715 ba[x] = char(qRed(rgb: *c++));
716 }
717 _stream.writeRawData(ba.data(), len: ba.size());
718 }
719
720 if (_zsize == 1) {
721 return _stream.status() == QDataStream::Ok;
722 }
723
724 if (_zsize != 2) {
725 for (y = 0; y < _ysize; y++) {
726 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: _ysize - y - 1));
727 for (x = 0; x < _xsize; x++) {
728 ba[x] = char(qGreen(rgb: *c++));
729 }
730 _stream.writeRawData(ba.data(), len: ba.size());
731 }
732
733 for (y = 0; y < _ysize; y++) {
734 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: _ysize - y - 1));
735 for (x = 0; x < _xsize; x++) {
736 ba[x] = char(qBlue(rgb: *c++));
737 }
738 _stream.writeRawData(ba.data(), len: ba.size());
739 }
740
741 if (_zsize == 3) {
742 return _stream.status() == QDataStream::Ok;
743 }
744 }
745
746 for (y = 0; y < _ysize; y++) {
747 c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image: img, y: _ysize - y - 1));
748 for (x = 0; x < _xsize; x++) {
749 ba[x] = char(qAlpha(rgb: *c++));
750 }
751 _stream.writeRawData(ba.data(), len: ba.size());
752 }
753
754 return _stream.status() == QDataStream::Ok;
755}
756
757bool SGIImagePrivate::writeImage(const QImage &image)
758{
759 if (image.allGray()) {
760 _dim = 2, _zsize = 1;
761 } else {
762 _dim = 3, _zsize = 3;
763 }
764
765 auto hasAlpha = image.hasAlphaChannel();
766 if (hasAlpha) {
767 _dim = 3, _zsize++;
768 }
769
770 auto tcs = QColorSpace();
771 auto tfmt = image.format();
772 auto cs = image.colorSpace();
773 if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
774 tcs = QColorSpace(QColorSpace::SRgb);
775 tfmt = QImage::Format_RGB32;
776 } else if (hasAlpha && tfmt != QImage::Format_ARGB32) {
777 tfmt = QImage::Format_ARGB32;
778 } else if (!hasAlpha && tfmt != QImage::Format_RGB32) {
779 tfmt = QImage::Format_RGB32;
780 }
781
782 const int w = image.width();
783 const int h = image.height();
784
785 if (w > 65535 || h > 65535) {
786 return false;
787 }
788
789 _magic = 0x01da;
790 _bpc = 1;
791 _xsize = w;
792 _ysize = h;
793 _pixmin = ~0u;
794 _pixmax = 0;
795 _colormap = NORMAL;
796 _numrows = _ysize * _zsize;
797 _starttab = new quint32[_numrows];
798 _rlemap.setBaseOffset(512 + _numrows * 2 * sizeof(quint32));
799
800 if (!scanData(img: image, tfmt, tcs)) {
801 // qCDebug(LOG_RGBPLUGIN) << "this can't happen";
802 return false;
803 }
804
805 _rlevector = _rlemap.vector();
806
807 long verbatim_size = _numrows * _xsize;
808 long rle_size = _numrows * 2 * sizeof(quint32);
809 for (int i = 0; i < _rlevector.size(); i++) {
810 rle_size += _rlevector[i]->size();
811 }
812
813 if (verbatim_size <= rle_size) {
814 return writeVerbatim(img: image, tfmt, tcs);
815 }
816 return writeRle();
817}
818
819///////////////////////////////////////////////////////////////////////////////
820
821RGBHandler::RGBHandler()
822 : QImageIOHandler()
823 , d(new SGIImagePrivate)
824{
825}
826
827bool RGBHandler::canRead() const
828{
829 if (canRead(device: device())) {
830 setFormat("rgb");
831 return true;
832 }
833 return false;
834}
835
836bool RGBHandler::read(QImage *outImage)
837{
838 d->setDevice(device());
839 return d->readImage(img&: *outImage);
840}
841
842bool RGBHandler::write(const QImage &image)
843{
844 d->setDevice(device());
845 return d->writeImage(image);
846}
847
848bool RGBHandler::supportsOption(ImageOption option) const
849{
850 if (option == QImageIOHandler::Size) {
851 return true;
852 }
853 if (option == QImageIOHandler::ImageFormat) {
854 return true;
855 }
856 return false;
857}
858
859QVariant RGBHandler::option(ImageOption option) const
860{
861 QVariant v;
862
863 if (option == QImageIOHandler::Size) {
864 auto &&sgi = d;
865 if (sgi->isSupported()) {
866 v = QVariant::fromValue(value: sgi->size());
867 } else if (auto dev = device()) {
868 if (d->peekHeader(device: dev) && sgi->isSupported()) {
869 v = QVariant::fromValue(value: sgi->size());
870 }
871 }
872 }
873
874 if (option == QImageIOHandler::ImageFormat) {
875 auto &&sgi = d;
876 if (sgi->isSupported()) {
877 v = QVariant::fromValue(value: sgi->format());
878 } else if (auto dev = device()) {
879 if (d->peekHeader(device: dev) && sgi->isSupported()) {
880 v = QVariant::fromValue(value: sgi->format());
881 }
882 }
883 }
884
885 return v;
886}
887
888bool RGBHandler::canRead(QIODevice *device)
889{
890 if (!device) {
891 qCWarning(LOG_RGBPLUGIN) << "RGBHandler::canRead() called with no device";
892 return false;
893 }
894
895 SGIImagePrivate sgi;
896 return sgi.peekHeader(device) && sgi.isSupported();
897}
898
899///////////////////////////////////////////////////////////////////////////////
900
901QImageIOPlugin::Capabilities RGBPlugin::capabilities(QIODevice *device, const QByteArray &format) const
902{
903 if (format == "rgb" || format == "rgba" || format == "bw" || format == "sgi") {
904 return Capabilities(CanRead | CanWrite);
905 }
906 if (!format.isEmpty()) {
907 return {};
908 }
909 if (!device->isOpen()) {
910 return {};
911 }
912
913 Capabilities cap;
914 if (device->isReadable() && RGBHandler::canRead(device)) {
915 cap |= CanRead;
916 }
917 if (device->isWritable()) {
918 cap |= CanWrite;
919 }
920 return cap;
921}
922
923QImageIOHandler *RGBPlugin::create(QIODevice *device, const QByteArray &format) const
924{
925 QImageIOHandler *handler = new RGBHandler;
926 handler->setDevice(device);
927 handler->setFormat(format);
928 return handler;
929}
930
931#include "moc_rgb_p.cpp"
932

source code of kimageformats/src/imageformats/rgb.cpp