Coverage Report

Created: 2026-06-14 06:57

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