KImageFormats

pcx.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2002-2005 Nadeem Hasan <nhasan@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "pcx_p.h"
9#include "util_p.h"
10
11#include <QColor>
12#include <QColorSpace>
13#include <QDataStream>
14#include <QDebug>
15#include <QImage>
16
17#pragma pack(push, 1)
18class RGB
19{
20public:
21 quint8 r;
22 quint8 g;
23 quint8 b;
24
25 static RGB from(const QRgb color)
26 {
27 RGB c;
28 c.r = qRed(color);
29 c.g = qGreen(color);
30 c.b = qBlue(color);
31 return c;
32 }
33};
34
35class Palette
36{
37public:
38 void setColor(int i, const QRgb color)
39 {
40 RGB &c = rgb[i];
41 c.r = qRed(color);
42 c.g = qGreen(color);
43 c.b = qBlue(color);
44 }
45
46 QRgb color(int i) const
47 {
48 return qRgb(rgb[i].r, rgb[i].g, rgb[i].b);
49 }
50
51 class RGB rgb[16];
52};
53
54class PCXHEADER
55{
56public:
57 PCXHEADER();
58
59 inline int width() const
60 {
61 return (XMax - XMin) + 1;
62 }
63 inline int height() const
64 {
65 return (YMax - YMin) + 1;
66 }
67 inline bool isCompressed() const
68 {
69 return (Encoding == 1);
70 }
71 /*!
72 * \brief isValid
73 * Checks if the header data are valid for the PCX.
74 * \note Put here the header sanity checks.
75 * \return True if the header is a valid PCX header, otherwise false.
76 */
77 inline bool isValid() const
78 {
79 return Manufacturer == 10 && BytesPerLine != 0;
80 }
81 /*!
82 * \brief isSupported
83 * \return True if the header is valid and the PCX format is supported by the plugin. Otherwise false.
84 */
85 inline bool isSupported() const
86 {
87 return isValid() && format() != QImage::Format_Invalid;
88 }
89 inline QImage::Format format() const
90 {
91 auto fmt = QImage::Format_Invalid;
92 if (Bpp == 1 && NPlanes == 1) {
94 } else if (Bpp == 1 && NPlanes == 4) {
96 } else if (Bpp == 1 && NPlanes == 3) {
98 } else if (Bpp == 4 && NPlanes == 1) {
100 } else if (Bpp == 2 && NPlanes == 1) {
102 } else if (Bpp == 8 && NPlanes == 1) {
104 } else if (Bpp == 8 && NPlanes == 3) {
106 } else if (Bpp == 8 && NPlanes == 4) {
108 }
109 return fmt;
110 }
111
112 quint8 Manufacturer; // Constant Flag, 10 = ZSoft .pcx
113 quint8 Version; // Version information·
114 // 0 = Version 2.5 of PC Paintbrush·
115 // 2 = Version 2.8 w/palette information·
116 // 3 = Version 2.8 w/o palette information·
117 // 4 = PC Paintbrush for Windows(Plus for
118 // Windows uses Ver 5)·
119 // 5 = Version 3.0 and > of PC Paintbrush
120 // and PC Paintbrush +, includes
121 // Publisher's Paintbrush . Includes
122 // 24-bit .PCX files·
123 quint8 Encoding; // 1 = .PCX run length encoding
124 quint8 Bpp; // Number of bits to represent a pixel
125 // (per Plane) - 1, 2, 4, or 8·
126 quint16 XMin;
127 quint16 YMin;
128 quint16 XMax;
129 quint16 YMax;
130 quint16 HDpi;
131 quint16 YDpi;
132 Palette ColorMap;
133 quint8 Reserved; // Should be set to 0.
134 quint8 NPlanes; // Number of color planes
135 quint16 BytesPerLine; // Number of bytes to allocate for a scanline
136 // plane. MUST be an EVEN number. Do NOT
137 // calculate from Xmax-Xmin.·
138 quint16 PaletteInfo; // How to interpret palette- 1 = Color/BW,
139 // 2 = Grayscale ( ignored in PB IV/ IV + )·
140 quint16 HScreenSize; // Horizontal screen size in pixels. New field
141 // found only in PB IV/IV Plus
142 quint16 VScreenSize; // Vertical screen size in pixels. New field
143 // found only in PB IV/IV Plus
144
145 quint8 unused[54];
146};
147
148#pragma pack(pop)
149
150static QDataStream &operator>>(QDataStream &s, RGB &rgb)
151{
152 quint8 r;
153 quint8 g;
154 quint8 b;
155
156 s >> r >> g >> b;
157 rgb.r = r;
158 rgb.g = g;
159 rgb.b = b;
160
161 return s;
162}
163
165{
166 for (int i = 0; i < 16; ++i) {
167 s >> pal.rgb[i];
168 }
169
170 return s;
171}
172
173static QDataStream &operator>>(QDataStream &s, PCXHEADER &ph)
174{
175 quint8 m;
176 quint8 ver;
177 quint8 enc;
178 quint8 bpp;
179 s >> m >> ver >> enc >> bpp;
180 ph.Manufacturer = m;
181 ph.Version = ver;
182 ph.Encoding = enc;
183 ph.Bpp = bpp;
184 quint16 xmin;
185 quint16 ymin;
186 quint16 xmax;
187 quint16 ymax;
188 s >> xmin >> ymin >> xmax >> ymax;
189 ph.XMin = xmin;
190 ph.YMin = ymin;
191 ph.XMax = xmax;
192 ph.YMax = ymax;
193 quint16 hdpi;
194 quint16 ydpi;
195 s >> hdpi >> ydpi;
196 ph.HDpi = hdpi;
197 ph.YDpi = ydpi;
198 Palette colorMap;
199 quint8 res;
200 quint8 np;
201 s >> colorMap >> res >> np;
202 ph.ColorMap = colorMap;
203 ph.Reserved = res;
204 ph.NPlanes = np;
205 quint16 bytesperline;
206 s >> bytesperline;
207 ph.BytesPerLine = bytesperline;
208 quint16 paletteinfo;
209 s >> paletteinfo;
210 ph.PaletteInfo = paletteinfo;
211 quint16 hscreensize;
212 quint16 vscreensize;
213 s >> hscreensize;
214 ph.HScreenSize = hscreensize;
215 s >> vscreensize;
216 ph.VScreenSize = vscreensize;
217
218 // Skip the rest of the header
219 for (size_t i = 0, n = sizeof(ph.unused); i < n; ++i) {
220 s >> ph.unused[i];
221 }
222
223 return s;
224}
225
226static QDataStream &operator<<(QDataStream &s, const RGB rgb)
227{
228 s << rgb.r << rgb.g << rgb.b;
229
230 return s;
231}
232
233static QDataStream &operator<<(QDataStream &s, const Palette &pal)
234{
235 for (int i = 0; i < 16; ++i) {
236 s << pal.rgb[i];
237 }
238
239 return s;
240}
241
242static QDataStream &operator<<(QDataStream &s, const PCXHEADER &ph)
243{
244 s << ph.Manufacturer;
245 s << ph.Version;
246 s << ph.Encoding;
247 s << ph.Bpp;
248 s << ph.XMin << ph.YMin << ph.XMax << ph.YMax;
249 s << ph.HDpi << ph.YDpi;
250 s << ph.ColorMap;
251 s << ph.Reserved;
252 s << ph.NPlanes;
253 s << ph.BytesPerLine;
254 s << ph.PaletteInfo;
255 s << ph.HScreenSize;
256 s << ph.VScreenSize;
257
258 for (size_t i = 0, n = sizeof(ph.unused); i < n; ++i) {
259 s << ph.unused[i];
260 }
261
262 return s;
263}
264
265PCXHEADER::PCXHEADER()
266{
267 // Initialize all data to zero
268 QByteArray dummy(128, 0);
269 dummy.fill(0);
271 s >> *this;
272}
273
274bool peekHeader(QIODevice *d, PCXHEADER& h)
275{
276 auto head = d->peek(sizeof(PCXHEADER));
277 if (size_t(head.size()) < sizeof(PCXHEADER)) {
278 return false;
279 }
280
281 QDataStream ds(head);
282 ds.setByteOrder(QDataStream::LittleEndian);
283 ds >> h;
284
285 return ds.status() == QDataStream::Ok && h.isValid();
286}
287
288static bool readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
289{
290 quint32 i = 0;
291 quint32 size = buf.size();
292 quint8 byte;
293 quint8 count;
294
295 if (header.isCompressed()) {
296 // Uncompress the image data
297 while (i < size) {
298 count = 1;
299 s >> byte;
300 if (byte > 0xc0) {
301 count = byte - 0xc0;
302 s >> byte;
303 }
304 while (count-- && i < size) {
305 buf[i++] = byte;
306 }
307 }
308 } else {
309 // Image is not compressed (possible?)
310 while (i < size) {
311 s >> byte;
312 buf[i++] = byte;
313 }
314 }
315
316 return (s.status() == QDataStream::Ok);
317}
318
319static bool readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
320{
321 QByteArray buf(header.BytesPerLine, 0);
322
323 img = imageAlloc(header.width(), header.height(), header.format());
324 img.setColorCount(2);
325
326 if (img.isNull()) {
327 qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
328 return false;
329 }
330
331 for (int y = 0; y < header.height(); ++y) {
332 if (s.atEnd()) {
333 return false;
334 }
335
336 if (!readLine(s, buf, header)) {
337 return false;
338 }
339
340 uchar *p = img.scanLine(y);
341 unsigned int bpl = qMin((quint16)((header.width() + 7) / 8), header.BytesPerLine);
342 for (unsigned int x = 0; x < bpl; ++x) {
343 p[x] = buf[x];
344 }
345 }
346
347 // Set the color palette
348 img.setColor(0, qRgb(0, 0, 0));
349 img.setColor(1, qRgb(255, 255, 255));
350
351 return true;
352}
353
354static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
355{
356 QByteArray buf(header.BytesPerLine * header.NPlanes, 0);
357 QByteArray pixbuf(header.width(), 0);
358
359 img = imageAlloc(header.width(), header.height(), header.format());
360 img.setColorCount(16);
361 if (img.isNull()) {
362 qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
363 return false;
364 }
365
366 if (header.BytesPerLine < (header.width() + 7) / 8) {
367 qWarning() << "PCX image has invalid BytesPerLine value";
368 return false;
369 }
370
371 for (int y = 0; y < header.height(); ++y) {
372 if (s.atEnd()) {
373 return false;
374 }
375
376 pixbuf.fill(0);
377 if (!readLine(s, buf, header)) {
378 return false;
379 }
380
381 for (int i = 0; i < header.NPlanes; i++) {
382 quint32 offset = i * header.BytesPerLine;
383 for (int x = 0; x < header.width(); ++x) {
384 if (buf[offset + (x / 8)] & (128 >> (x % 8))) {
385 pixbuf[x] = (int)(pixbuf[x]) + (1 << i);
386 }
387 }
388 }
389
390 uchar *p = img.scanLine(y);
391 if (!p) {
392 qWarning() << "Failed to get scanline for" << y << "might be out of bounds";
393 }
394 for (int x = 0; x < header.width(); ++x) {
395 p[x] = pixbuf[x];
396 }
397 }
398
399 // Read the palette
400 for (int i = 0; i < 16; ++i) {
401 img.setColor(i, header.ColorMap.color(i));
402 }
403
404 return true;
405}
406
407static bool readImage2(QImage &img, QDataStream &s, const PCXHEADER &header)
408{
409 QByteArray buf(header.BytesPerLine, 0);
410
411 img = imageAlloc(header.width(), header.height(), header.format());
412 img.setColorCount(4);
413
414 if (img.isNull()) {
415 qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
416 return false;
417 }
418
419 for (int y = 0; y < header.height(); ++y) {
420 if (s.atEnd()) {
421 return false;
422 }
423
424 if (!readLine(s, buf, header)) {
425 return false;
426 }
427
428 uchar *p = img.scanLine(y);
429 if (!p) {
430 return false;
431 }
432
433 const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width() / 4));
434 for (unsigned int x = 0; x < bpl; ++x) {
435 p[x * 4] = (buf[x] >> 6) & 3;
436 p[x * 4 + 1] = (buf[x] >> 4) & 3;
437 p[x * 4 + 2] = (buf[x] >> 2) & 3;
438 p[x * 4 + 3] = buf[x] & 3;
439 }
440 }
441
442 // Read the palette
443 for (int i = 0; i < 4; ++i) {
444 img.setColor(i, header.ColorMap.color(i));
445 }
446
447 return (s.status() == QDataStream::Ok);
448}
449
450static bool readImage4v2(QImage &img, QDataStream &s, const PCXHEADER &header)
451{
452 QByteArray buf(header.BytesPerLine, 0);
453
454 img = imageAlloc(header.width(), header.height(), header.format());
455 img.setColorCount(16);
456
457 if (img.isNull()) {
458 qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
459 return false;
460 }
461
462 for (int y = 0; y < header.height(); ++y) {
463 if (s.atEnd()) {
464 return false;
465 }
466
467 if (!readLine(s, buf, header)) {
468 return false;
469 }
470
471 uchar *p = img.scanLine(y);
472 if (!p) {
473 return false;
474 }
475
476 const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width() / 2));
477 for (unsigned int x = 0; x < bpl; ++x) {
478 p[x * 2] = (buf[x] & 240) >> 4;
479 p[x * 2 + 1] = buf[x] & 15;
480 }
481 }
482
483 // Read the palette
484 for (int i = 0; i < 16; ++i) {
485 img.setColor(i, header.ColorMap.color(i));
486 }
487
488 return (s.status() == QDataStream::Ok);
489}
490
491static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
492{
493 QByteArray buf(header.BytesPerLine, 0);
494
495 img = imageAlloc(header.width(), header.height(), header.format());
496 img.setColorCount(256);
497
498 if (img.isNull()) {
499 qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
500 return false;
501 }
502
503 for (int y = 0; y < header.height(); ++y) {
504 if (s.atEnd()) {
505 return false;
506 }
507
508 if (!readLine(s, buf, header)) {
509 return false;
510 }
511
512 uchar *p = img.scanLine(y);
513 if (!p) {
514 return false;
515 }
516
517 unsigned int bpl = qMin(header.BytesPerLine, (quint16)header.width());
518 for (unsigned int x = 0; x < bpl; ++x) {
519 p[x] = buf[x];
520 }
521 }
522
523 // by specification, the extended palette starts at file.size() - 769
524 quint8 flag = 0;
525 if (auto device = s.device()) {
526 if (device->isSequential()) {
527 while (flag != 12 && s.status() == QDataStream::Ok) {
528 s >> flag;
529 }
530 }
531 else {
532 device->seek(device->size() - 769);
533 s >> flag;
534 }
535 }
536
537 // qDebug() << "Palette Flag: " << flag;
538 if (flag == 12 && (header.Version == 5 || header.Version == 2)) {
539 // Read the palette
540 quint8 r;
541 quint8 g;
542 quint8 b;
543 for (int i = 0; i < 256; ++i) {
544 s >> r >> g >> b;
545 img.setColor(i, qRgb(r, g, b));
546 }
547 }
548
549 return (s.status() == QDataStream::Ok);
550}
551
552static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
553{
554 QByteArray r_buf(header.BytesPerLine, 0);
555 QByteArray g_buf(header.BytesPerLine, 0);
556 QByteArray b_buf(header.BytesPerLine, 0);
557 QByteArray a_buf(header.BytesPerLine, char(0xFF));
558
559 img = imageAlloc(header.width(), header.height(), header.format());
560
561 if (img.isNull()) {
562 qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
563 return false;
564 }
565
566 const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width()));
567
568 for (int y = 0; y < header.height(); ++y) {
569 if (s.atEnd()) {
570 return false;
571 }
572
573 if (!readLine(s, r_buf, header)) {
574 return false;
575 }
576 if (!readLine(s, g_buf, header)) {
577 return false;
578 }
579 if (!readLine(s, b_buf, header)) {
580 return false;
581 }
582 if (header.NPlanes == 4 && !readLine(s, a_buf, header)) {
583 return false;
584 }
585
586 auto p = reinterpret_cast<QRgb *>(img.scanLine(y));
587 for (unsigned int x = 0; x < bpl; ++x) {
588 p[x] = qRgba(r_buf[x], g_buf[x], b_buf[x], a_buf[x]);
589 }
590 }
591
592 return true;
593}
594
595static bool writeLine(QDataStream &s, QByteArray &buf)
596{
597 quint32 i = 0;
598 quint32 size = buf.size();
599 quint8 count;
600 quint8 data;
601 char byte;
602
603 while (i < size) {
604 count = 1;
605 byte = buf[i++];
606
607 while ((i < size) && (byte == buf[i]) && (count < 63)) {
608 ++i;
609 ++count;
610 }
611
612 data = byte;
613
614 if (count > 1 || data >= 0xc0) {
615 count |= 0xc0;
616 s << count;
617 }
618
619 s << data;
620 }
621 return (s.status() == QDataStream::Ok);
622}
623
624static bool writeImage1(QImage &img, QDataStream &s, PCXHEADER &header)
625{
626 if (img.format() != QImage::Format_Mono) {
628 }
629 if (img.isNull() || img.colorCount() < 1) {
630 return false;
631 }
632 auto rgb = img.color(0);
633 auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127;
634
635 header.Bpp = 1;
636 header.NPlanes = 1;
637 header.BytesPerLine = img.bytesPerLine();
638 if (header.BytesPerLine == 0) {
639 return false;
640 }
641
642 s << header;
643
644 QByteArray buf(header.BytesPerLine, 0);
645
646 for (int y = 0; y < header.height(); ++y) {
647 auto p = img.constScanLine(y);
648
649 // Invert as QImage uses reverse palette for monochrome images?
650 for (int i = 0; i < header.BytesPerLine; ++i) {
651 buf[i] = minIsBlack ? p[i] : ~p[i];
652 }
653
654 if (!writeLine(s, buf)) {
655 return false;
656 }
657 }
658 return true;
659}
660
661static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header)
662{
663 header.Bpp = 1;
664 header.NPlanes = 4;
665 header.BytesPerLine = header.width() / 8;
666 if (header.BytesPerLine == 0) {
667 return false;
668 }
669
670 for (int i = 0; i < 16; ++i) {
671 header.ColorMap.setColor(i, img.color(i));
672 }
673
674 s << header;
675
676 QByteArray buf[4];
677
678 for (int i = 0; i < 4; ++i) {
679 buf[i].resize(header.BytesPerLine);
680 }
681
682 for (int y = 0; y < header.height(); ++y) {
683 auto p = img.constScanLine(y);
684
685 for (int i = 0; i < 4; ++i) {
686 buf[i].fill(0);
687 }
688
689 for (int x = 0; x < header.width(); ++x) {
690 for (int i = 0; i < 4; ++i) {
691 if (*(p + x) & (1 << i)) {
692 buf[i][x / 8] = (int)(buf[i][x / 8]) | 1 << (7 - x % 8);
693 }
694 }
695 }
696
697 for (int i = 0; i < 4; ++i) {
698 if (!writeLine(s, buf[i])) {
699 return false;
700 }
701 }
702 }
703 return true;
704}
705
706static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
707{
708 if (img.format() == QImage::Format_Grayscale16) {
710 }
711 if (img.isNull()) {
712 return false;
713 }
714
715 header.Bpp = 8;
716 header.NPlanes = 1;
717 header.BytesPerLine = img.bytesPerLine();
718 if (header.BytesPerLine == 0) {
719 return false;
720 }
721
722 s << header;
723
724 QByteArray buf(header.BytesPerLine, 0);
725
726 for (int y = 0; y < header.height(); ++y) {
727 auto p = img.constScanLine(y);
728
729 for (int i = 0; i < header.BytesPerLine; ++i) {
730 buf[i] = p[i];
731 }
732
733 if (!writeLine(s, buf)) {
734 return false;
735 }
736 }
737
738 // Write palette flag
739 quint8 byte = 12;
740 s << byte;
741
742 // Write palette
743 for (int i = 0; i < 256; ++i) {
744 if (img.format() != QImage::Format_Indexed8)
745 s << RGB::from(qRgb(i, i, i));
746 else
747 s << RGB::from(img.color(i));
748 }
749
750 return (s.status() == QDataStream::Ok);
751}
752
753static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
754{
755 auto hasAlpha = img.hasAlphaChannel();
756 header.Bpp = 8;
757 header.NPlanes = hasAlpha ? 4 : 3;
758 header.BytesPerLine = header.width();
759 if (header.BytesPerLine == 0) {
760 return false;
761 }
762
763 if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB32) {
765 }
766 if (img.isNull()) {
767 return false;
768 }
769
770 s << header;
771
772 QByteArray r_buf(header.width(), 0);
773 QByteArray g_buf(header.width(), 0);
774 QByteArray b_buf(header.width(), 0);
775 QByteArray a_buf(header.width(), char(0xFF));
776
777 for (int y = 0; y < header.height(); ++y) {
778 auto p = reinterpret_cast<const QRgb *>(img.constScanLine(y));
779
780 for (int x = 0; x < header.width(); ++x) {
781 auto &&rgb = p[x];
782 r_buf[x] = qRed(rgb);
783 g_buf[x] = qGreen(rgb);
784 b_buf[x] = qBlue(rgb);
785 a_buf[x] = qAlpha(rgb);
786 }
787
788 if (!writeLine(s, r_buf)) {
789 return false;
790 }
791 if (!writeLine(s, g_buf)) {
792 return false;
793 }
794 if (!writeLine(s, b_buf)) {
795 return false;
796 }
797 if (hasAlpha && !writeLine(s, a_buf)) {
798 return false;
799 }
800 }
801
802 return true;
803}
804
805class PCXHandlerPrivate
806{
807public:
808 PCXHandlerPrivate() {}
809 ~PCXHandlerPrivate() {}
810
811 PCXHEADER m_header;
812};
813
814PCXHandler::PCXHandler()
816 , d(new PCXHandlerPrivate)
817{
818}
819
820bool PCXHandler::canRead() const
821{
822 if (canRead(device())) {
823 setFormat("pcx");
824 return true;
825 }
826 return false;
827}
828
829bool PCXHandler::read(QImage *outImage)
830{
831 QDataStream s(device());
833
834 if (s.device()->size() < 128) {
835 return false;
836 }
837
838 auto&& header = d->m_header;
839 s >> header;
840
841 if (s.status() != QDataStream::Ok || s.atEnd()) {
842 return false;
843 }
844
845 if (!header.isSupported()) {
846 return false;
847 }
848
849 auto ok = false;
850 QImage img;
851 if (header.Bpp == 1 && header.NPlanes == 1) {
852 ok = readImage1(img, s, header);
853 } else if (header.Bpp == 1 && (header.NPlanes == 4 || header.NPlanes == 3)) {
854 ok = readImage4(img, s, header);
855 } else if (header.Bpp == 2 && header.NPlanes == 1) {
856 ok = readImage2(img, s, header);
857 } else if (header.Bpp == 4 && header.NPlanes == 1) {
858 ok = readImage4v2(img, s, header);
859 } else if (header.Bpp == 8 && header.NPlanes == 1) {
860 ok = readImage8(img, s, header);
861 } else if (header.Bpp == 8 && (header.NPlanes == 3 || header.NPlanes == 4)) {
862 ok = readImage24(img, s, header);
863 }
864
865 if (img.isNull() || !ok) {
866 return false;
867 }
868
869 img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000));
870 img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000));
871 *outImage = img;
872 return true;
873}
874
875bool PCXHandler::write(const QImage &image)
876{
877 QDataStream s(device());
879
880 QImage img = image;
881#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
882 auto cs = image.colorSpace();
883 if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
885 }
886#endif
887
888 const int w = img.width();
889 const int h = img.height();
890
891 if (w > 65536 || h > 65536) {
892 return false;
893 }
894
895 PCXHEADER header;
896
897 header.Manufacturer = 10;
898 header.Version = 5;
899 header.Encoding = 1;
900 header.XMin = 0;
901 header.YMin = 0;
902 header.XMax = w - 1;
903 header.YMax = h - 1;
904 header.HDpi = qRound(image.dotsPerMeterX() * 25.4 / 1000);
905 header.YDpi = qRound(image.dotsPerMeterY() * 25.4 / 1000);
906 header.Reserved = 0;
907 header.PaletteInfo = 1;
908
909 auto ok = false;
910 if (img.depth() == 1) {
911 ok = writeImage1(img, s, header);
912 } else if (img.format() == QImage::Format_Indexed8 && img.colorCount() <= 16) {
913 ok = writeImage4(img, s, header);
914 } else if (img.depth() == 8 || img.format() == QImage::Format_Grayscale16) {
915 ok = writeImage8(img, s, header);
916 } else if (img.depth() >= 16) {
917 ok = writeImage24(img, s, header);
918 }
919
920 return ok;
921}
922
923bool PCXHandler::supportsOption(ImageOption option) const
924{
925 if (option == QImageIOHandler::Size) {
926 return true;
927 }
928 if (option == QImageIOHandler::ImageFormat) {
929 return true;
930 }
931 return false;
932}
933
934QVariant PCXHandler::option(ImageOption option) const
935{
936 QVariant v;
937
938 if (option == QImageIOHandler::Size) {
939 auto&& header = d->m_header;
940 if (header.isSupported()) {
941 v = QVariant::fromValue(QSize(header.width(), header.height()));
942 } else if (auto dev = device()) {
943 if (peekHeader(dev, header) && header.isSupported()) {
944 v = QVariant::fromValue(QSize(header.width(), header.height()));
945 }
946 }
947 }
948
949 if (option == QImageIOHandler::ImageFormat) {
950 auto&& header = d->m_header;
951 if (header.isSupported()) {
952 v = QVariant::fromValue(header.format());
953 } else if (auto dev = device()) {
954 if (peekHeader(dev, header) && header.isSupported()) {
955 v = QVariant::fromValue(header.format());
956 }
957 }
958 }
959
960 return v;
961}
962
963bool PCXHandler::canRead(QIODevice *device)
964{
965 if (!device) {
966 qWarning("PCXHandler::canRead() called with no device");
967 return false;
968 }
969
970 PCXHEADER header;
971 if (!peekHeader(device, header)) {
972 return false;
973 }
974 return header.isSupported();
975}
976
977QImageIOPlugin::Capabilities PCXPlugin::capabilities(QIODevice *device, const QByteArray &format) const
978{
979 if (format == "pcx") {
980 return Capabilities(CanRead | CanWrite);
981 }
982 if (!format.isEmpty()) {
983 return {};
984 }
985 if (!device->isOpen()) {
986 return {};
987 }
988
989 Capabilities cap;
990 if (device->isReadable() && PCXHandler::canRead(device)) {
991 cap |= CanRead;
992 }
993 if (device->isWritable()) {
994 cap |= CanWrite;
995 }
996 return cap;
997}
998
999QImageIOHandler *PCXPlugin::create(QIODevice *device, const QByteArray &format) const
1000{
1001 QImageIOHandler *handler = new PCXHandler;
1002 handler->setDevice(device);
1003 handler->setFormat(format);
1004 return handler;
1005}
1006
1007#include "moc_pcx_p.cpp"
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
QDebug operator<<(QDebug dbg, const DcrawInfoContainer &c)
QFlags< Capability > Capabilities
QByteArray & fill(char ch, qsizetype size)
bool isEmpty() const const
void resize(qsizetype newSize, char c)
qsizetype size() const const
bool atEnd() const const
QIODevice * device() const const
void setByteOrder(ByteOrder bo)
Status status() const const
qsizetype bytesPerLine() const const
QRgb color(int i) const const
int colorCount() const const
QColorSpace colorSpace() const const
const uchar * constScanLine(int i) const const
void convertTo(Format format, Qt::ImageConversionFlags flags)
QImage convertedToColorSpace(const QColorSpace &colorSpace) const const
int depth() const const
int dotsPerMeterX() const const
int dotsPerMeterY() const const
Format format() const const
bool hasAlphaChannel() const const
int height() const const
bool isNull() const const
uchar * scanLine(int i)
void setColor(int index, QRgb colorValue)
void setColorCount(int colorCount)
void setDotsPerMeterX(int x)
void setDotsPerMeterY(int y)
int width() const const
void setDevice(QIODevice *device)
void setFormat(const QByteArray &format)
bool isOpen() const const
bool isReadable() const const
bool isWritable() const const
QByteArray peek(qint64 maxSize)
virtual qint64 size() const const
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:00:39 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.