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 "util_p.h" |
24 | |
25 | #include <QList> |
26 | #include <QMap> |
27 | |
28 | #include <QDebug> |
29 | #include <QImage> |
30 | |
31 | class RLEData : public QList<uchar> |
32 | { |
33 | public: |
34 | RLEData() |
35 | { |
36 | } |
37 | RLEData(const uchar *d, uint l, uint o) |
38 | : _offset(o) |
39 | { |
40 | for (uint i = 0; i < l; i++) { |
41 | append(t: d[i]); |
42 | } |
43 | } |
44 | bool operator<(const RLEData &) const; |
45 | void write(QDataStream &s); |
46 | uint offset() const |
47 | { |
48 | return _offset; |
49 | } |
50 | |
51 | private: |
52 | uint _offset; |
53 | }; |
54 | |
55 | class RLEMap : public QMap<RLEData, uint> |
56 | { |
57 | public: |
58 | RLEMap() |
59 | : _counter(0) |
60 | , _offset(0) |
61 | { |
62 | } |
63 | uint insert(const uchar *d, uint l); |
64 | QList<const RLEData *> vector(); |
65 | void setBaseOffset(uint o) |
66 | { |
67 | _offset = o; |
68 | } |
69 | |
70 | private: |
71 | uint _counter; |
72 | uint _offset; |
73 | }; |
74 | |
75 | class SGIImage |
76 | { |
77 | public: |
78 | SGIImage(QIODevice *device); |
79 | ~SGIImage(); |
80 | |
81 | bool readImage(QImage &); |
82 | bool writeImage(const QImage &); |
83 | |
84 | private: |
85 | enum { |
86 | NORMAL, |
87 | DITHERED, |
88 | SCREEN, |
89 | COLORMAP, |
90 | }; // colormap |
91 | QIODevice *_dev; |
92 | QDataStream _stream; |
93 | |
94 | quint8 _rle; |
95 | quint8 _bpc; |
96 | quint16 _dim; |
97 | quint16 _xsize; |
98 | quint16 _ysize; |
99 | quint16 _zsize; |
100 | quint32 _pixmin; |
101 | quint32 _pixmax; |
102 | char _imagename[80]; |
103 | quint32 _colormap; |
104 | |
105 | quint32 *_starttab; |
106 | quint32 *_lengthtab; |
107 | QByteArray _data; |
108 | QByteArray::Iterator _pos; |
109 | RLEMap _rlemap; |
110 | QList<const RLEData *> _rlevector; |
111 | uint _numrows; |
112 | |
113 | bool readData(QImage &); |
114 | bool getRow(uchar *dest); |
115 | |
116 | void writeHeader(); |
117 | void writeRle(); |
118 | void writeVerbatim(const QImage &); |
119 | bool scanData(const QImage &); |
120 | uint compact(uchar *, uchar *); |
121 | uchar intensity(uchar); |
122 | }; |
123 | |
124 | SGIImage::SGIImage(QIODevice *io) |
125 | : _starttab(nullptr) |
126 | , _lengthtab(nullptr) |
127 | { |
128 | _dev = io; |
129 | _stream.setDevice(_dev); |
130 | } |
131 | |
132 | SGIImage::~SGIImage() |
133 | { |
134 | delete[] _starttab; |
135 | delete[] _lengthtab; |
136 | } |
137 | |
138 | /////////////////////////////////////////////////////////////////////////////// |
139 | |
140 | bool SGIImage::getRow(uchar *dest) |
141 | { |
142 | int n; |
143 | int i; |
144 | if (!_rle) { |
145 | for (i = 0; i < _xsize; i++) { |
146 | if (_pos >= _data.end()) { |
147 | return false; |
148 | } |
149 | dest[i] = uchar(*_pos); |
150 | _pos += _bpc; |
151 | } |
152 | return true; |
153 | } |
154 | |
155 | for (i = 0; i < _xsize;) { |
156 | if (_bpc == 2) { |
157 | _pos++; |
158 | } |
159 | if (_pos >= _data.end()) { |
160 | return false; |
161 | } |
162 | n = *_pos & 0x7f; |
163 | if (!n) { |
164 | break; |
165 | } |
166 | |
167 | if (*_pos++ & 0x80) { |
168 | for (; i < _xsize && _pos < _data.end() && n--; i++) { |
169 | *dest++ = *_pos; |
170 | _pos += _bpc; |
171 | } |
172 | } else { |
173 | for (; i < _xsize && n--; i++) { |
174 | *dest++ = *_pos; |
175 | } |
176 | |
177 | _pos += _bpc; |
178 | } |
179 | } |
180 | return i == _xsize; |
181 | } |
182 | |
183 | bool SGIImage::readData(QImage &img) |
184 | { |
185 | QRgb *c; |
186 | quint32 *start = _starttab; |
187 | QByteArray lguard(_xsize, 0); |
188 | uchar *line = (uchar *)lguard.data(); |
189 | unsigned x; |
190 | unsigned y; |
191 | |
192 | if (!_rle) { |
193 | _pos = _data.begin(); |
194 | } |
195 | |
196 | for (y = 0; y < _ysize; y++) { |
197 | if (_rle) { |
198 | _pos = _data.begin() + *start++; |
199 | } |
200 | if (!getRow(dest: line)) { |
201 | return false; |
202 | } |
203 | c = (QRgb *)img.scanLine(_ysize - y - 1); |
204 | for (x = 0; x < _xsize; x++, c++) { |
205 | *c = qRgb(r: line[x], g: line[x], b: line[x]); |
206 | } |
207 | } |
208 | |
209 | if (_zsize == 1) { |
210 | return true; |
211 | } |
212 | |
213 | if (_zsize != 2) { |
214 | for (y = 0; y < _ysize; y++) { |
215 | if (_rle) { |
216 | _pos = _data.begin() + *start++; |
217 | } |
218 | if (!getRow(dest: line)) { |
219 | return false; |
220 | } |
221 | c = (QRgb *)img.scanLine(_ysize - y - 1); |
222 | for (x = 0; x < _xsize; x++, c++) { |
223 | *c = qRgb(r: qRed(rgb: *c), g: line[x], b: line[x]); |
224 | } |
225 | } |
226 | |
227 | for (y = 0; y < _ysize; y++) { |
228 | if (_rle) { |
229 | _pos = _data.begin() + *start++; |
230 | } |
231 | if (!getRow(dest: line)) { |
232 | return false; |
233 | } |
234 | c = (QRgb *)img.scanLine(_ysize - y - 1); |
235 | for (x = 0; x < _xsize; x++, c++) { |
236 | *c = qRgb(r: qRed(rgb: *c), g: qGreen(rgb: *c), b: line[x]); |
237 | } |
238 | } |
239 | |
240 | if (_zsize == 3) { |
241 | return true; |
242 | } |
243 | } |
244 | |
245 | for (y = 0; y < _ysize; y++) { |
246 | if (_rle) { |
247 | _pos = _data.begin() + *start++; |
248 | } |
249 | if (!getRow(dest: line)) { |
250 | return false; |
251 | } |
252 | c = (QRgb *)img.scanLine(_ysize - y - 1); |
253 | for (x = 0; x < _xsize; x++, c++) { |
254 | *c = qRgba(r: qRed(rgb: *c), g: qGreen(rgb: *c), b: qBlue(rgb: *c), a: line[x]); |
255 | } |
256 | } |
257 | |
258 | return true; |
259 | } |
260 | |
261 | bool SGIImage::readImage(QImage &img) |
262 | { |
263 | qint8 u8; |
264 | qint16 u16; |
265 | qint32 u32; |
266 | |
267 | // qDebug() << "reading rgb "; |
268 | |
269 | // magic |
270 | _stream >> u16; |
271 | if (u16 != 0x01da) { |
272 | return false; |
273 | } |
274 | |
275 | // verbatim/rle |
276 | _stream >> _rle; |
277 | // qDebug() << (_rle ? "RLE" : "verbatim"); |
278 | if (_rle > 1) { |
279 | return false; |
280 | } |
281 | |
282 | // bytes per channel |
283 | _stream >> _bpc; |
284 | // qDebug() << "bytes per channel: " << int(_bpc); |
285 | if (_bpc == 1) { |
286 | ; |
287 | } else if (_bpc == 2) { |
288 | // qDebug() << "dropping least significant byte"; |
289 | } else { |
290 | return false; |
291 | } |
292 | |
293 | // number of dimensions |
294 | _stream >> _dim; |
295 | // qDebug() << "dimensions: " << _dim; |
296 | if (_dim < 1 || _dim > 3) { |
297 | return false; |
298 | } |
299 | |
300 | _stream >> _xsize >> _ysize >> _zsize >> _pixmin >> _pixmax >> u32; |
301 | // qDebug() << "x: " << _xsize; |
302 | // qDebug() << "y: " << _ysize; |
303 | // qDebug() << "z: " << _zsize; |
304 | |
305 | // name |
306 | _stream.readRawData(_imagename, len: 80); |
307 | _imagename[79] = '\0'; |
308 | |
309 | _stream >> _colormap; |
310 | // qDebug() << "colormap: " << _colormap; |
311 | if (_colormap != NORMAL) { |
312 | return false; // only NORMAL supported |
313 | } |
314 | |
315 | for (int i = 0; i < 404; i++) { |
316 | _stream >> u8; |
317 | } |
318 | |
319 | if (_dim == 1) { |
320 | // qDebug() << "1-dimensional images aren't supported yet"; |
321 | return false; |
322 | } |
323 | |
324 | if (_stream.atEnd()) { |
325 | return false; |
326 | } |
327 | |
328 | img = imageAlloc(width: _xsize, height: _ysize, format: QImage::Format_RGB32); |
329 | if (img.isNull()) { |
330 | qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(_xsize, _ysize); |
331 | return false; |
332 | } |
333 | |
334 | if (_zsize == 0) { |
335 | return false; |
336 | } |
337 | |
338 | if (_zsize == 2 || _zsize == 4) { |
339 | img = img.convertToFormat(f: QImage::Format_ARGB32); |
340 | } else if (_zsize > 4) { |
341 | // qDebug() << "using first 4 of " << _zsize << " channels"; |
342 | // Only let this continue if it won't cause a int overflow later |
343 | // this is most likely a broken file anyway |
344 | if (_ysize > std::numeric_limits<int>::max() / _zsize) { |
345 | return false; |
346 | } |
347 | } |
348 | |
349 | _numrows = _ysize * _zsize; |
350 | |
351 | if (_rle) { |
352 | uint l; |
353 | _starttab = new quint32[_numrows]; |
354 | for (l = 0; !_stream.atEnd() && l < _numrows; l++) { |
355 | _stream >> _starttab[l]; |
356 | _starttab[l] -= 512 + _numrows * 2 * sizeof(quint32); |
357 | } |
358 | for (; l < _numrows; l++) { |
359 | _starttab[l] = 0; |
360 | } |
361 | |
362 | _lengthtab = new quint32[_numrows]; |
363 | for (l = 0; l < _numrows; l++) { |
364 | _stream >> _lengthtab[l]; |
365 | } |
366 | } |
367 | |
368 | _data = _dev->readAll(); |
369 | |
370 | // sanity check |
371 | if (_rle) { |
372 | for (uint o = 0; o < _numrows; o++) { |
373 | // don't change to greater-or-equal! |
374 | if (_starttab[o] + _lengthtab[o] > (uint)_data.size()) { |
375 | // qDebug() << "image corrupt (sanity check failed)"; |
376 | return false; |
377 | } |
378 | } |
379 | } |
380 | |
381 | if (!readData(img)) { |
382 | // qDebug() << "image corrupt (incomplete scanline)"; |
383 | return false; |
384 | } |
385 | |
386 | return true; |
387 | } |
388 | |
389 | /////////////////////////////////////////////////////////////////////////////// |
390 | |
391 | void RLEData::write(QDataStream &s) |
392 | { |
393 | for (int i = 0; i < size(); i++) { |
394 | s << at(i); |
395 | } |
396 | } |
397 | |
398 | bool RLEData::operator<(const RLEData &b) const |
399 | { |
400 | uchar ac; |
401 | uchar bc; |
402 | for (int i = 0; i < qMin(a: size(), b: b.size()); i++) { |
403 | ac = at(i); |
404 | bc = b[i]; |
405 | if (ac != bc) { |
406 | return ac < bc; |
407 | } |
408 | } |
409 | return size() < b.size(); |
410 | } |
411 | |
412 | uint RLEMap::insert(const uchar *d, uint l) |
413 | { |
414 | RLEData data = RLEData(d, l, _offset); |
415 | Iterator it = find(key: data); |
416 | if (it != end()) { |
417 | return it.value(); |
418 | } |
419 | |
420 | _offset += l; |
421 | return QMap<RLEData, uint>::insert(key: data, value: _counter++).value(); |
422 | } |
423 | |
424 | QList<const RLEData *> RLEMap::vector() |
425 | { |
426 | QList<const RLEData *> v(size()); |
427 | for (Iterator it = begin(); it != end(); ++it) { |
428 | v.replace(i: it.value(), t: &it.key()); |
429 | } |
430 | |
431 | return v; |
432 | } |
433 | |
434 | uchar SGIImage::intensity(uchar c) |
435 | { |
436 | if (c < _pixmin) { |
437 | _pixmin = c; |
438 | } |
439 | if (c > _pixmax) { |
440 | _pixmax = c; |
441 | } |
442 | return c; |
443 | } |
444 | |
445 | uint SGIImage::compact(uchar *d, uchar *s) |
446 | { |
447 | uchar *dest = d; |
448 | uchar *src = s; |
449 | uchar patt; |
450 | uchar *t; |
451 | uchar *end = s + _xsize; |
452 | int i; |
453 | int n; |
454 | while (src < end) { |
455 | for (n = 0, t = src; t + 2 < end && !(*t == t[1] && *t == t[2]); t++) { |
456 | n++; |
457 | } |
458 | |
459 | while (n) { |
460 | i = n > 126 ? 126 : n; |
461 | n -= i; |
462 | *dest++ = 0x80 | i; |
463 | while (i--) { |
464 | *dest++ = *src++; |
465 | } |
466 | } |
467 | |
468 | if (src == end) { |
469 | break; |
470 | } |
471 | |
472 | patt = *src++; |
473 | for (n = 1; src < end && *src == patt; src++) { |
474 | n++; |
475 | } |
476 | |
477 | while (n) { |
478 | i = n > 126 ? 126 : n; |
479 | n -= i; |
480 | *dest++ = i; |
481 | *dest++ = patt; |
482 | } |
483 | } |
484 | *dest++ = 0; |
485 | return dest - d; |
486 | } |
487 | |
488 | bool SGIImage::scanData(const QImage &img) |
489 | { |
490 | quint32 *start = _starttab; |
491 | QByteArray lineguard(_xsize * 2, 0); |
492 | QByteArray bufguard(_xsize, 0); |
493 | uchar *line = (uchar *)lineguard.data(); |
494 | uchar *buf = (uchar *)bufguard.data(); |
495 | const QRgb *c; |
496 | unsigned x; |
497 | unsigned y; |
498 | uint len; |
499 | |
500 | for (y = 0; y < _ysize; y++) { |
501 | const int yPos = _ysize - y - 1; // scanline doesn't do any sanity checking |
502 | if (yPos >= img.height()) { |
503 | qWarning() << "Failed to get scanline for" << yPos; |
504 | return false; |
505 | } |
506 | |
507 | c = reinterpret_cast<const QRgb *>(img.scanLine(yPos)); |
508 | |
509 | for (x = 0; x < _xsize; x++) { |
510 | buf[x] = intensity(c: qRed(rgb: *c++)); |
511 | } |
512 | len = compact(d: line, s: buf); |
513 | *start++ = _rlemap.insert(d: line, l: len); |
514 | } |
515 | |
516 | if (_zsize == 1) { |
517 | return true; |
518 | } |
519 | |
520 | if (_zsize != 2) { |
521 | for (y = 0; y < _ysize; y++) { |
522 | const int yPos = _ysize - y - 1; |
523 | if (yPos >= img.height()) { |
524 | qWarning() << "Failed to get scanline for" << yPos; |
525 | return false; |
526 | } |
527 | |
528 | c = reinterpret_cast<const QRgb *>(img.scanLine(yPos)); |
529 | for (x = 0; x < _xsize; x++) { |
530 | buf[x] = intensity(c: qGreen(rgb: *c++)); |
531 | } |
532 | len = compact(d: line, s: buf); |
533 | *start++ = _rlemap.insert(d: line, l: len); |
534 | } |
535 | |
536 | for (y = 0; y < _ysize; y++) { |
537 | const int yPos = _ysize - y - 1; |
538 | if (yPos >= img.height()) { |
539 | qWarning() << "Failed to get scanline for" << yPos; |
540 | return false; |
541 | } |
542 | |
543 | c = reinterpret_cast<const QRgb *>(img.scanLine(yPos)); |
544 | for (x = 0; x < _xsize; x++) { |
545 | buf[x] = intensity(c: qBlue(rgb: *c++)); |
546 | } |
547 | len = compact(d: line, s: buf); |
548 | *start++ = _rlemap.insert(d: line, l: len); |
549 | } |
550 | |
551 | if (_zsize == 3) { |
552 | return true; |
553 | } |
554 | } |
555 | |
556 | for (y = 0; y < _ysize; y++) { |
557 | const int yPos = _ysize - y - 1; |
558 | if (yPos >= img.height()) { |
559 | qWarning() << "Failed to get scanline for" << yPos; |
560 | return false; |
561 | } |
562 | |
563 | c = reinterpret_cast<const QRgb *>(img.scanLine(yPos)); |
564 | for (x = 0; x < _xsize; x++) { |
565 | buf[x] = intensity(c: qAlpha(rgb: *c++)); |
566 | } |
567 | len = compact(d: line, s: buf); |
568 | *start++ = _rlemap.insert(d: line, l: len); |
569 | } |
570 | |
571 | return true; |
572 | } |
573 | |
574 | void SGIImage::() |
575 | { |
576 | _stream << quint16(0x01da); |
577 | _stream << _rle << _bpc << _dim; |
578 | _stream << _xsize << _ysize << _zsize; |
579 | _stream << _pixmin << _pixmax; |
580 | _stream << quint32(0); |
581 | |
582 | for (int i = 0; i < 80; i++) { |
583 | _imagename[i] = '\0'; |
584 | } |
585 | _stream.writeRawData(_imagename, len: 80); |
586 | |
587 | _stream << _colormap; |
588 | for (int i = 0; i < 404; i++) { |
589 | _stream << quint8(0); |
590 | } |
591 | } |
592 | |
593 | void SGIImage::writeRle() |
594 | { |
595 | _rle = 1; |
596 | // qDebug() << "writing RLE data"; |
597 | writeHeader(); |
598 | uint i; |
599 | |
600 | // write start table |
601 | for (i = 0; i < _numrows; i++) { |
602 | _stream << quint32(_rlevector[_starttab[i]]->offset()); |
603 | } |
604 | |
605 | // write length table |
606 | for (i = 0; i < _numrows; i++) { |
607 | _stream << quint32(_rlevector[_starttab[i]]->size()); |
608 | } |
609 | |
610 | // write data |
611 | for (i = 0; (int)i < _rlevector.size(); i++) { |
612 | const_cast<RLEData *>(_rlevector[i])->write(s&: _stream); |
613 | } |
614 | } |
615 | |
616 | void SGIImage::writeVerbatim(const QImage &img) |
617 | { |
618 | _rle = 0; |
619 | // qDebug() << "writing verbatim data"; |
620 | writeHeader(); |
621 | |
622 | const QRgb *c; |
623 | unsigned x; |
624 | unsigned y; |
625 | |
626 | for (y = 0; y < _ysize; y++) { |
627 | c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1)); |
628 | for (x = 0; x < _xsize; x++) { |
629 | _stream << quint8(qRed(rgb: *c++)); |
630 | } |
631 | } |
632 | |
633 | if (_zsize == 1) { |
634 | return; |
635 | } |
636 | |
637 | if (_zsize != 2) { |
638 | for (y = 0; y < _ysize; y++) { |
639 | c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1)); |
640 | for (x = 0; x < _xsize; x++) { |
641 | _stream << quint8(qGreen(rgb: *c++)); |
642 | } |
643 | } |
644 | |
645 | for (y = 0; y < _ysize; y++) { |
646 | c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1)); |
647 | for (x = 0; x < _xsize; x++) { |
648 | _stream << quint8(qBlue(rgb: *c++)); |
649 | } |
650 | } |
651 | |
652 | if (_zsize == 3) { |
653 | return; |
654 | } |
655 | } |
656 | |
657 | for (y = 0; y < _ysize; y++) { |
658 | c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1)); |
659 | for (x = 0; x < _xsize; x++) { |
660 | _stream << quint8(qAlpha(rgb: *c++)); |
661 | } |
662 | } |
663 | } |
664 | |
665 | bool SGIImage::writeImage(const QImage &image) |
666 | { |
667 | // qDebug() << "writing "; // TODO add filename |
668 | QImage img = image; |
669 | if (img.allGray()) { |
670 | _dim = 2, _zsize = 1; |
671 | } else { |
672 | _dim = 3, _zsize = 3; |
673 | } |
674 | |
675 | auto hasAlpha = img.hasAlphaChannel(); |
676 | if (hasAlpha) { |
677 | _dim = 3, _zsize++; |
678 | } |
679 | |
680 | if (hasAlpha && img.format() != QImage::Format_ARGB32) { |
681 | img = img.convertToFormat(f: QImage::Format_ARGB32); |
682 | } else if (!hasAlpha && img.format() != QImage::Format_RGB32) { |
683 | img = img.convertToFormat(f: QImage::Format_RGB32); |
684 | } |
685 | if (img.isNull()) { |
686 | // qDebug() << "can't convert image to depth 32"; |
687 | return false; |
688 | } |
689 | |
690 | const int w = img.width(); |
691 | const int h = img.height(); |
692 | |
693 | if (w > 65535 || h > 65535) { |
694 | return false; |
695 | } |
696 | |
697 | _bpc = 1; |
698 | _xsize = w; |
699 | _ysize = h; |
700 | _pixmin = ~0u; |
701 | _pixmax = 0; |
702 | _colormap = NORMAL; |
703 | _numrows = _ysize * _zsize; |
704 | _starttab = new quint32[_numrows]; |
705 | _rlemap.setBaseOffset(512 + _numrows * 2 * sizeof(quint32)); |
706 | |
707 | if (!scanData(img)) { |
708 | // qDebug() << "this can't happen"; |
709 | return false; |
710 | } |
711 | |
712 | _rlevector = _rlemap.vector(); |
713 | |
714 | long verbatim_size = _numrows * _xsize; |
715 | long rle_size = _numrows * 2 * sizeof(quint32); |
716 | for (int i = 0; i < _rlevector.size(); i++) { |
717 | rle_size += _rlevector[i]->size(); |
718 | } |
719 | |
720 | if (verbatim_size <= rle_size) { |
721 | writeVerbatim(img); |
722 | } else { |
723 | writeRle(); |
724 | } |
725 | return true; |
726 | } |
727 | |
728 | /////////////////////////////////////////////////////////////////////////////// |
729 | |
730 | RGBHandler::RGBHandler() |
731 | { |
732 | } |
733 | |
734 | bool RGBHandler::canRead() const |
735 | { |
736 | if (canRead(device: device())) { |
737 | setFormat("rgb" ); |
738 | return true; |
739 | } |
740 | return false; |
741 | } |
742 | |
743 | bool RGBHandler::read(QImage *outImage) |
744 | { |
745 | SGIImage sgi(device()); |
746 | return sgi.readImage(img&: *outImage); |
747 | } |
748 | |
749 | bool RGBHandler::write(const QImage &image) |
750 | { |
751 | SGIImage sgi(device()); |
752 | return sgi.writeImage(image); |
753 | } |
754 | |
755 | bool RGBHandler::canRead(QIODevice *device) |
756 | { |
757 | if (!device) { |
758 | qWarning(msg: "RGBHandler::canRead() called with no device" ); |
759 | return false; |
760 | } |
761 | |
762 | const qint64 oldPos = device->pos(); |
763 | const QByteArray head = device->readLine(maxlen: 64); |
764 | int readBytes = head.size(); |
765 | |
766 | if (device->isSequential()) { |
767 | while (readBytes > 0) { |
768 | device->ungetChar(c: head[readBytes-- - 1]); |
769 | } |
770 | |
771 | } else { |
772 | device->seek(pos: oldPos); |
773 | } |
774 | |
775 | return head.size() >= 4 && head.startsWith(bv: "\x01\xda" ) && (head[2] == 0 || head[2] == 1) && (head[3] == 1 || head[3] == 2); |
776 | } |
777 | |
778 | /////////////////////////////////////////////////////////////////////////////// |
779 | |
780 | QImageIOPlugin::Capabilities RGBPlugin::capabilities(QIODevice *device, const QByteArray &format) const |
781 | { |
782 | if (format == "rgb" || format == "rgba" || format == "bw" || format == "sgi" ) { |
783 | return Capabilities(CanRead | CanWrite); |
784 | } |
785 | if (!format.isEmpty()) { |
786 | return {}; |
787 | } |
788 | if (!device->isOpen()) { |
789 | return {}; |
790 | } |
791 | |
792 | Capabilities cap; |
793 | if (device->isReadable() && RGBHandler::canRead(device)) { |
794 | cap |= CanRead; |
795 | } |
796 | if (device->isWritable()) { |
797 | cap |= CanWrite; |
798 | } |
799 | return cap; |
800 | } |
801 | |
802 | QImageIOHandler *RGBPlugin::create(QIODevice *device, const QByteArray &format) const |
803 | { |
804 | QImageIOHandler *handler = new RGBHandler; |
805 | handler->setDevice(device); |
806 | handler->setFormat(format); |
807 | return handler; |
808 | } |
809 | |
810 | #include "moc_rgb_p.cpp" |
811 | |