File indexing completed on 2024-04-28 15:25:42

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2002-2005 Nadeem Hasan <nhasan@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "pcx_p.h"
0009 #include "util_p.h"
0010 
0011 #include <QColor>
0012 #include <QDataStream>
0013 #include <QDebug>
0014 #include <QImage>
0015 
0016 #pragma pack(push, 1)
0017 class RGB
0018 {
0019 public:
0020     quint8 r;
0021     quint8 g;
0022     quint8 b;
0023 
0024     static RGB from(const QRgb color)
0025     {
0026         RGB c;
0027         c.r = qRed(color);
0028         c.g = qGreen(color);
0029         c.b = qBlue(color);
0030         return c;
0031     }
0032 };
0033 
0034 class Palette
0035 {
0036 public:
0037     void setColor(int i, const QRgb color)
0038     {
0039         RGB &c = rgb[i];
0040         c.r = qRed(color);
0041         c.g = qGreen(color);
0042         c.b = qBlue(color);
0043     }
0044 
0045     QRgb color(int i) const
0046     {
0047         return qRgb(rgb[i].r, rgb[i].g, rgb[i].b);
0048     }
0049 
0050     class RGB rgb[16];
0051 };
0052 
0053 class PCXHEADER
0054 {
0055 public:
0056     PCXHEADER();
0057 
0058     inline int width() const
0059     {
0060         return (XMax - XMin) + 1;
0061     }
0062     inline int height() const
0063     {
0064         return (YMax - YMin) + 1;
0065     }
0066     inline bool isCompressed() const
0067     {
0068         return (Encoding == 1);
0069     }
0070 
0071     quint8 Manufacturer; // Constant Flag, 10 = ZSoft .pcx
0072     quint8 Version; // Version information·
0073     // 0 = Version 2.5 of PC Paintbrush·
0074     // 2 = Version 2.8 w/palette information·
0075     // 3 = Version 2.8 w/o palette information·
0076     // 4 = PC Paintbrush for Windows(Plus for
0077     //     Windows uses Ver 5)·
0078     // 5 = Version 3.0 and > of PC Paintbrush
0079     //     and PC Paintbrush +, includes
0080     //     Publisher's Paintbrush . Includes
0081     //     24-bit .PCX files·
0082     quint8 Encoding; // 1 = .PCX run length encoding
0083     quint8 Bpp; // Number of bits to represent a pixel
0084     // (per Plane) - 1, 2, 4, or 8·
0085     quint16 XMin;
0086     quint16 YMin;
0087     quint16 XMax;
0088     quint16 YMax;
0089     quint16 HDpi;
0090     quint16 YDpi;
0091     Palette ColorMap;
0092     quint8 Reserved; // Should be set to 0.
0093     quint8 NPlanes; // Number of color planes
0094     quint16 BytesPerLine; // Number of bytes to allocate for a scanline
0095     // plane.  MUST be an EVEN number.  Do NOT
0096     // calculate from Xmax-Xmin.·
0097     quint16 PaletteInfo; // How to interpret palette- 1 = Color/BW,
0098     // 2 = Grayscale ( ignored in PB IV/ IV + )·
0099     quint16 HScreenSize; // Horizontal screen size in pixels. New field
0100     // found only in PB IV/IV Plus
0101     quint16 VScreenSize; // Vertical screen size in pixels. New field
0102     // found only in PB IV/IV Plus
0103 };
0104 
0105 #pragma pack(pop)
0106 
0107 static QDataStream &operator>>(QDataStream &s, RGB &rgb)
0108 {
0109     quint8 r;
0110     quint8 g;
0111     quint8 b;
0112 
0113     s >> r >> g >> b;
0114     rgb.r = r;
0115     rgb.g = g;
0116     rgb.b = b;
0117 
0118     return s;
0119 }
0120 
0121 static QDataStream &operator>>(QDataStream &s, Palette &pal)
0122 {
0123     for (int i = 0; i < 16; ++i) {
0124         s >> pal.rgb[i];
0125     }
0126 
0127     return s;
0128 }
0129 
0130 static QDataStream &operator>>(QDataStream &s, PCXHEADER &ph)
0131 {
0132     quint8 m;
0133     quint8 ver;
0134     quint8 enc;
0135     quint8 bpp;
0136     s >> m >> ver >> enc >> bpp;
0137     ph.Manufacturer = m;
0138     ph.Version = ver;
0139     ph.Encoding = enc;
0140     ph.Bpp = bpp;
0141     quint16 xmin;
0142     quint16 ymin;
0143     quint16 xmax;
0144     quint16 ymax;
0145     s >> xmin >> ymin >> xmax >> ymax;
0146     ph.XMin = xmin;
0147     ph.YMin = ymin;
0148     ph.XMax = xmax;
0149     ph.YMax = ymax;
0150     quint16 hdpi;
0151     quint16 ydpi;
0152     s >> hdpi >> ydpi;
0153     ph.HDpi = hdpi;
0154     ph.YDpi = ydpi;
0155     Palette colorMap;
0156     quint8 res;
0157     quint8 np;
0158     s >> colorMap >> res >> np;
0159     ph.ColorMap = colorMap;
0160     ph.Reserved = res;
0161     ph.NPlanes = np;
0162     quint16 bytesperline;
0163     s >> bytesperline;
0164     ph.BytesPerLine = bytesperline;
0165     quint16 paletteinfo;
0166     s >> paletteinfo;
0167     ph.PaletteInfo = paletteinfo;
0168     quint16 hscreensize;
0169     quint16 vscreensize;
0170     s >> hscreensize;
0171     ph.HScreenSize = hscreensize;
0172     s >> vscreensize;
0173     ph.VScreenSize = vscreensize;
0174 
0175     // Skip the rest of the header
0176     quint8 byte;
0177     for (auto i = 0; i < 54; ++i) {
0178         s >> byte;
0179     }
0180 
0181     return s;
0182 }
0183 
0184 static QDataStream &operator<<(QDataStream &s, const RGB rgb)
0185 {
0186     s << rgb.r << rgb.g << rgb.b;
0187 
0188     return s;
0189 }
0190 
0191 static QDataStream &operator<<(QDataStream &s, const Palette &pal)
0192 {
0193     for (int i = 0; i < 16; ++i) {
0194         s << pal.rgb[i];
0195     }
0196 
0197     return s;
0198 }
0199 
0200 static QDataStream &operator<<(QDataStream &s, const PCXHEADER &ph)
0201 {
0202     s << ph.Manufacturer;
0203     s << ph.Version;
0204     s << ph.Encoding;
0205     s << ph.Bpp;
0206     s << ph.XMin << ph.YMin << ph.XMax << ph.YMax;
0207     s << ph.HDpi << ph.YDpi;
0208     s << ph.ColorMap;
0209     s << ph.Reserved;
0210     s << ph.NPlanes;
0211     s << ph.BytesPerLine;
0212     s << ph.PaletteInfo;
0213     s << ph.HScreenSize;
0214     s << ph.VScreenSize;
0215 
0216     quint8 byte = 0;
0217     for (int i = 0; i < 54; ++i) {
0218         s << byte;
0219     }
0220 
0221     return s;
0222 }
0223 
0224 PCXHEADER::PCXHEADER()
0225 {
0226     // Initialize all data to zero
0227     QByteArray dummy(128, 0);
0228     dummy.fill(0);
0229     QDataStream s(&dummy, QIODevice::ReadOnly);
0230     s >> *this;
0231 }
0232 
0233 static bool readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
0234 {
0235     quint32 i = 0;
0236     quint32 size = buf.size();
0237     quint8 byte;
0238     quint8 count;
0239 
0240     if (header.isCompressed()) {
0241         // Uncompress the image data
0242         while (i < size) {
0243             count = 1;
0244             s >> byte;
0245             if (byte > 0xc0) {
0246                 count = byte - 0xc0;
0247                 s >> byte;
0248             }
0249             while (count-- && i < size) {
0250                 buf[i++] = byte;
0251             }
0252         }
0253     } else {
0254         // Image is not compressed (possible?)
0255         while (i < size) {
0256             s >> byte;
0257             buf[i++] = byte;
0258         }
0259     }
0260 
0261     return (s.status() == QDataStream::Ok);
0262 }
0263 
0264 static bool readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
0265 {
0266     QByteArray buf(header.BytesPerLine, 0);
0267 
0268     img = imageAlloc(header.width(), header.height(), QImage::Format_Mono);
0269     img.setColorCount(2);
0270 
0271     if (img.isNull()) {
0272         qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
0273         return false;
0274     }
0275 
0276     for (int y = 0; y < header.height(); ++y) {
0277         if (s.atEnd()) {
0278             return false;
0279         }
0280 
0281         if (!readLine(s, buf, header)) {
0282             return false;
0283         }
0284 
0285         uchar *p = img.scanLine(y);
0286         unsigned int bpl = qMin((quint16)((header.width() + 7) / 8), header.BytesPerLine);
0287         for (unsigned int x = 0; x < bpl; ++x) {
0288             p[x] = buf[x];
0289         }
0290     }
0291 
0292     // Set the color palette
0293     img.setColor(0, qRgb(0, 0, 0));
0294     img.setColor(1, qRgb(255, 255, 255));
0295 
0296     return true;
0297 }
0298 
0299 static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
0300 {
0301     QByteArray buf(header.BytesPerLine * 4, 0);
0302     QByteArray pixbuf(header.width(), 0);
0303 
0304     img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
0305     img.setColorCount(16);
0306     if (img.isNull()) {
0307         qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
0308         return false;
0309     }
0310 
0311     for (int y = 0; y < header.height(); ++y) {
0312         if (s.atEnd()) {
0313             return false;
0314         }
0315 
0316         pixbuf.fill(0);
0317         if (!readLine(s, buf, header)) {
0318             return false;
0319         }
0320 
0321         for (int i = 0; i < 4; i++) {
0322             quint32 offset = i * header.BytesPerLine;
0323             for (int x = 0; x < header.width(); ++x) {
0324                 if (buf[offset + (x / 8)] & (128 >> (x % 8))) {
0325                     pixbuf[x] = (int)(pixbuf[x]) + (1 << i);
0326                 }
0327             }
0328         }
0329 
0330         uchar *p = img.scanLine(y);
0331         if (!p) {
0332             qWarning() << "Failed to get scanline for" << y << "might be out of bounds";
0333         }
0334         for (int x = 0; x < header.width(); ++x) {
0335             p[x] = pixbuf[x];
0336         }
0337     }
0338 
0339     // Read the palette
0340     for (int i = 0; i < 16; ++i) {
0341         img.setColor(i, header.ColorMap.color(i));
0342     }
0343 
0344     return true;
0345 }
0346 
0347 static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
0348 {
0349     QByteArray buf(header.BytesPerLine, 0);
0350 
0351     img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
0352     img.setColorCount(256);
0353 
0354     if (img.isNull()) {
0355         qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
0356         return false;
0357     }
0358 
0359     for (int y = 0; y < header.height(); ++y) {
0360         if (s.atEnd()) {
0361             return false;
0362         }
0363 
0364         if (!readLine(s, buf, header)) {
0365             return false;
0366         }
0367 
0368         uchar *p = img.scanLine(y);
0369         if (!p) {
0370             return false;
0371         }
0372 
0373         unsigned int bpl = qMin(header.BytesPerLine, (quint16)header.width());
0374         for (unsigned int x = 0; x < bpl; ++x) {
0375             p[x] = buf[x];
0376         }
0377     }
0378 
0379     // by specification, the extended palette starts at file.size() - 769
0380     quint8 flag = 0;
0381     if (auto device = s.device()) {
0382         if (device->isSequential()) {
0383             while (flag != 12 && s.status() == QDataStream::Ok) {
0384                 s >> flag;
0385             }
0386         }
0387         else {
0388             device->seek(device->size() - 769);
0389             s >> flag;
0390         }
0391     }
0392 
0393     //   qDebug() << "Palette Flag: " << flag;
0394     if (flag == 12 && (header.Version == 5 || header.Version == 2)) {
0395         // Read the palette
0396         quint8 r;
0397         quint8 g;
0398         quint8 b;
0399         for (int i = 0; i < 256; ++i) {
0400             s >> r >> g >> b;
0401             img.setColor(i, qRgb(r, g, b));
0402         }
0403     }
0404 
0405     return (s.status() == QDataStream::Ok);
0406 }
0407 
0408 static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
0409 {
0410     QByteArray r_buf(header.BytesPerLine, 0);
0411     QByteArray g_buf(header.BytesPerLine, 0);
0412     QByteArray b_buf(header.BytesPerLine, 0);
0413 
0414     img = imageAlloc(header.width(), header.height(), QImage::Format_RGB32);
0415 
0416     if (img.isNull()) {
0417         qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
0418         return false;
0419     }
0420 
0421     for (int y = 0; y < header.height(); ++y) {
0422         if (s.atEnd()) {
0423             return false;
0424         }
0425 
0426         if (!readLine(s, r_buf, header)) {
0427             return false;
0428         }
0429         if (!readLine(s, g_buf, header)) {
0430             return false;
0431         }
0432         if (!readLine(s, b_buf, header)) {
0433             return false;
0434         }
0435 
0436         uint *p = (uint *)img.scanLine(y);
0437         for (int x = 0; x < header.width(); ++x) {
0438             p[x] = qRgb(r_buf[x], g_buf[x], b_buf[x]);
0439         }
0440     }
0441 
0442     return true;
0443 }
0444 
0445 static bool writeLine(QDataStream &s, QByteArray &buf)
0446 {
0447     quint32 i = 0;
0448     quint32 size = buf.size();
0449     quint8 count;
0450     quint8 data;
0451     char byte;
0452 
0453     while (i < size) {
0454         count = 1;
0455         byte = buf[i++];
0456 
0457         while ((i < size) && (byte == buf[i]) && (count < 63)) {
0458             ++i;
0459             ++count;
0460         }
0461 
0462         data = byte;
0463 
0464         if (count > 1 || data >= 0xc0) {
0465             count |= 0xc0;
0466             s << count;
0467         }
0468 
0469         s << data;
0470     }
0471     return (s.status() == QDataStream::Ok);
0472 }
0473 
0474 static bool writeImage1(QImage &img, QDataStream &s, PCXHEADER &header)
0475 {
0476     if (img.format() != QImage::Format_Mono) {
0477         img = img.convertToFormat(QImage::Format_Mono);
0478     }
0479     if (img.isNull() || img.colorCount() < 1) {
0480         return false;
0481     }
0482     auto rgb = img.color(0);
0483     auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127;
0484 
0485     header.Bpp = 1;
0486     header.NPlanes = 1;
0487     header.BytesPerLine = img.bytesPerLine();
0488     if (header.BytesPerLine == 0) {
0489         return false;
0490     }
0491 
0492     s << header;
0493 
0494     QByteArray buf(header.BytesPerLine, 0);
0495 
0496     for (int y = 0; y < header.height(); ++y) {
0497         quint8 *p = img.scanLine(y);
0498 
0499         // Invert as QImage uses reverse palette for monochrome images?
0500         for (int i = 0; i < header.BytesPerLine; ++i) {
0501             buf[i] = minIsBlack ? p[i] : ~p[i];
0502         }
0503 
0504         if (!writeLine(s, buf)) {
0505             return false;
0506         }
0507     }
0508     return true;
0509 }
0510 
0511 static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header)
0512 {
0513     header.Bpp = 1;
0514     header.NPlanes = 4;
0515     header.BytesPerLine = header.width() / 8;
0516     if (header.BytesPerLine == 0) {
0517         return false;
0518     }
0519 
0520     for (int i = 0; i < 16; ++i) {
0521         header.ColorMap.setColor(i, img.color(i));
0522     }
0523 
0524     s << header;
0525 
0526     QByteArray buf[4];
0527 
0528     for (int i = 0; i < 4; ++i) {
0529         buf[i].resize(header.BytesPerLine);
0530     }
0531 
0532     for (int y = 0; y < header.height(); ++y) {
0533         quint8 *p = img.scanLine(y);
0534 
0535         for (int i = 0; i < 4; ++i) {
0536             buf[i].fill(0);
0537         }
0538 
0539         for (int x = 0; x < header.width(); ++x) {
0540             for (int i = 0; i < 4; ++i) {
0541                 if (*(p + x) & (1 << i)) {
0542                     buf[i][x / 8] = (int)(buf[i][x / 8]) | 1 << (7 - x % 8);
0543                 }
0544             }
0545         }
0546 
0547         for (int i = 0; i < 4; ++i) {
0548             if (!writeLine(s, buf[i])) {
0549                 return false;
0550             }
0551         }
0552     }
0553     return true;
0554 }
0555 
0556 static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
0557 {
0558     header.Bpp = 8;
0559     header.NPlanes = 1;
0560     header.BytesPerLine = img.bytesPerLine();
0561     if (header.BytesPerLine == 0) {
0562         return false;
0563     }
0564 
0565     s << header;
0566 
0567     QByteArray buf(header.BytesPerLine, 0);
0568 
0569     for (int y = 0; y < header.height(); ++y) {
0570         quint8 *p = img.scanLine(y);
0571 
0572         for (int i = 0; i < header.BytesPerLine; ++i) {
0573             buf[i] = p[i];
0574         }
0575 
0576         if (!writeLine(s, buf)) {
0577             return false;
0578         }
0579     }
0580 
0581     // Write palette flag
0582     quint8 byte = 12;
0583     s << byte;
0584 
0585     // Write palette
0586     for (int i = 0; i < 256; ++i) {
0587         s << RGB::from(img.color(i));
0588     }
0589 
0590     return (s.status() == QDataStream::Ok);
0591 }
0592 
0593 static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
0594 {
0595     header.Bpp = 8;
0596     header.NPlanes = 3;
0597     header.BytesPerLine = header.width();
0598     if (header.BytesPerLine == 0) {
0599         return false;
0600     }
0601 
0602     if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB32) {
0603         img = img.convertToFormat(QImage::Format_RGB32);
0604     }
0605     if (img.isNull()) {
0606         return false;
0607     }
0608 
0609     s << header;
0610 
0611     QByteArray r_buf(header.width(), 0);
0612     QByteArray g_buf(header.width(), 0);
0613     QByteArray b_buf(header.width(), 0);
0614 
0615     for (int y = 0; y < header.height(); ++y) {
0616         auto p = (QRgb*)img.scanLine(y);
0617 
0618         for (int x = 0; x < header.width(); ++x) {
0619             QRgb rgb = *p++;
0620             r_buf[x] = qRed(rgb);
0621             g_buf[x] = qGreen(rgb);
0622             b_buf[x] = qBlue(rgb);
0623         }
0624 
0625         if (!writeLine(s, r_buf)) {
0626             return false;
0627         }
0628         if (!writeLine(s, g_buf)) {
0629             return false;
0630         }
0631         if (!writeLine(s, b_buf)) {
0632             return false;
0633         }
0634     }
0635 
0636     return true;
0637 }
0638 
0639 PCXHandler::PCXHandler()
0640 {
0641 }
0642 
0643 bool PCXHandler::canRead() const
0644 {
0645     if (canRead(device())) {
0646         setFormat("pcx");
0647         return true;
0648     }
0649     return false;
0650 }
0651 
0652 bool PCXHandler::read(QImage *outImage)
0653 {
0654     QDataStream s(device());
0655     s.setByteOrder(QDataStream::LittleEndian);
0656 
0657     if (s.device()->size() < 128) {
0658         return false;
0659     }
0660 
0661     PCXHEADER header;
0662 
0663     s >> header;
0664 
0665     if (header.Manufacturer != 10 || header.BytesPerLine == 0 || s.atEnd()) {
0666         return false;
0667     }
0668 
0669     auto ok = false;
0670     QImage img;
0671     if (header.Bpp == 1 && header.NPlanes == 1) {
0672         ok = readImage1(img, s, header);
0673     } else if (header.Bpp == 1 && header.NPlanes == 4) {
0674         ok = readImage4(img, s, header);
0675     } else if (header.Bpp == 8 && header.NPlanes == 1) {
0676         ok = readImage8(img, s, header);
0677     } else if (header.Bpp == 8 && header.NPlanes == 3) {
0678         ok = readImage24(img, s, header);
0679     }
0680 
0681     if (img.isNull() || !ok) {
0682         return false;
0683     }
0684 
0685     img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000));
0686     img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000));
0687     *outImage = img;
0688     return true;
0689 }
0690 
0691 bool PCXHandler::write(const QImage &image)
0692 {
0693     QDataStream s(device());
0694     s.setByteOrder(QDataStream::LittleEndian);
0695 
0696     QImage img = image;
0697 
0698     const int w = img.width();
0699     const int h = img.height();
0700 
0701     if (w > 65536 || h > 65536) {
0702         return false;
0703     }
0704 
0705     PCXHEADER header;
0706 
0707     header.Manufacturer = 10;
0708     header.Version = 5;
0709     header.Encoding = 1;
0710     header.XMin = 0;
0711     header.YMin = 0;
0712     header.XMax = w - 1;
0713     header.YMax = h - 1;
0714     header.HDpi = qRound(image.dotsPerMeterX() * 25.4 / 1000);
0715     header.YDpi = qRound(image.dotsPerMeterY() * 25.4 / 1000);
0716     header.Reserved = 0;
0717     header.PaletteInfo = 1;
0718 
0719     auto ok = false;
0720     if (img.depth() == 1) {
0721         ok = writeImage1(img, s, header);
0722     } else if (img.depth() == 8 && img.colorCount() <= 16) {
0723         ok = writeImage4(img, s, header);
0724     } else if (img.depth() == 8) {
0725         ok = writeImage8(img, s, header);
0726     } else if (img.depth() >= 24) {
0727         ok = writeImage24(img, s, header);
0728     }
0729 
0730     return ok;
0731 }
0732 
0733 bool PCXHandler::canRead(QIODevice *device)
0734 {
0735     if (!device) {
0736         qWarning("PCXHandler::canRead() called with no device");
0737         return false;
0738     }
0739 
0740     qint64 oldPos = device->pos();
0741 
0742     char head[1];
0743     qint64 readBytes = device->read(head, sizeof(head));
0744     if (readBytes != sizeof(head)) {
0745         if (device->isSequential()) {
0746             while (readBytes > 0) {
0747                 device->ungetChar(head[readBytes-- - 1]);
0748             }
0749         } else {
0750             device->seek(oldPos);
0751         }
0752         return false;
0753     }
0754 
0755     if (device->isSequential()) {
0756         while (readBytes > 0) {
0757             device->ungetChar(head[readBytes-- - 1]);
0758         }
0759     } else {
0760         device->seek(oldPos);
0761     }
0762 
0763     return qstrncmp(head, "\012", 1) == 0;
0764 }
0765 
0766 QImageIOPlugin::Capabilities PCXPlugin::capabilities(QIODevice *device, const QByteArray &format) const
0767 {
0768     if (format == "pcx") {
0769         return Capabilities(CanRead | CanWrite);
0770     }
0771     if (!format.isEmpty()) {
0772         return {};
0773     }
0774     if (!device->isOpen()) {
0775         return {};
0776     }
0777 
0778     Capabilities cap;
0779     if (device->isReadable() && PCXHandler::canRead(device)) {
0780         cap |= CanRead;
0781     }
0782     if (device->isWritable()) {
0783         cap |= CanWrite;
0784     }
0785     return cap;
0786 }
0787 
0788 QImageIOHandler *PCXPlugin::create(QIODevice *device, const QByteArray &format) const
0789 {
0790     QImageIOHandler *handler = new PCXHandler;
0791     handler->setDevice(device);
0792     handler->setFormat(format);
0793     return handler;
0794 }
0795 
0796 #include "moc_pcx_p.cpp"