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"