00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "pcx.h"
00011
00012 #include <QtGui/QImage>
00013
00014 #include <kdebug.h>
00015
00016 static QDataStream &operator>>( QDataStream &s, RGB &rgb )
00017 {
00018 quint8 r, g, b;
00019
00020 s >> r >> g >> b;
00021 rgb.r = r;
00022 rgb.g = g;
00023 rgb.b = b;
00024
00025 return s;
00026 }
00027
00028 static QDataStream &operator>>( QDataStream &s, Palette &pal )
00029 {
00030 for ( int i=0; i<16; ++i )
00031 s >> pal.rgb[ i ];
00032
00033 return s;
00034 }
00035
00036 static QDataStream &operator>>( QDataStream &s, PCXHEADER &ph )
00037 {
00038 quint8 m, ver, enc, bpp;
00039 s >> m >> ver >> enc >> bpp;
00040 ph.Manufacturer = m;
00041 ph.Version = ver;
00042 ph.Encoding = enc;
00043 ph.Bpp = bpp;
00044 quint16 xmin, ymin, xmax, ymax;
00045 s >> xmin >> ymin >> xmax >> ymax;
00046 ph.XMin = xmin;
00047 ph.YMin = ymin;
00048 ph.XMax = xmax;
00049 ph.YMax = ymax;
00050 quint16 hdpi, ydpi;
00051 s >> hdpi >> ydpi;
00052 ph.HDpi = hdpi;
00053 ph.YDpi = ydpi;
00054 Palette colorMap;
00055 quint8 res, np;
00056 s >> colorMap >> res >> np;
00057 ph.ColorMap = colorMap;
00058 ph.Reserved = res;
00059 ph.NPlanes = np;
00060 quint16 bytesperline;
00061 s >> bytesperline; ph.BytesPerLine = bytesperline;
00062 quint16 paletteinfo;
00063 s >> paletteinfo; ph.PaletteInfo = paletteinfo;
00064 quint16 hscreensize, vscreensize;
00065 s >> hscreensize; ph.HScreenSize = hscreensize;
00066 s >> vscreensize; ph.VScreenSize = vscreensize;
00067
00068
00069 quint8 byte;
00070 while ( s.device()->pos() < 128 )
00071 s >> byte;
00072
00073 return s;
00074 }
00075
00076 static QDataStream &operator<<( QDataStream &s, const RGB &rgb )
00077 {
00078 s << rgb.r << rgb.g << rgb.b;
00079
00080 return s;
00081 }
00082
00083 static QDataStream &operator<<( QDataStream &s, const Palette &pal )
00084 {
00085 for ( int i=0; i<16; ++i )
00086 s << pal.rgb[ i ];
00087
00088 return s;
00089 }
00090
00091 static QDataStream &operator<<( QDataStream &s, const PCXHEADER &ph )
00092 {
00093 s << ph.Manufacturer;
00094 s << ph.Version;
00095 s << ph.Encoding;
00096 s << ph.Bpp;
00097 s << ph.XMin << ph.YMin << ph.XMax << ph.YMax;
00098 s << ph.HDpi << ph.YDpi;
00099 s << ph.ColorMap;
00100 s << ph.Reserved;
00101 s << ph.NPlanes;
00102 s << ph.BytesPerLine;
00103 s << ph.PaletteInfo;
00104 s << ph.HScreenSize;
00105 s << ph.VScreenSize;
00106
00107 quint8 byte = 0;
00108 for ( int i=0; i<54; ++i )
00109 s << byte;
00110
00111 return s;
00112 }
00113
00114 PCXHEADER::PCXHEADER()
00115 {
00116
00117 QByteArray dummy( 128, 0 );
00118 dummy.fill( 0 );
00119 QDataStream s( &dummy, QIODevice::ReadOnly );
00120 s >> *this;
00121 }
00122
00123 static void readLine( QDataStream &s, QByteArray &buf, const PCXHEADER &header )
00124 {
00125 quint32 i=0;
00126 quint32 size = buf.size();
00127 quint8 byte, count;
00128
00129 if ( header.isCompressed() )
00130 {
00131
00132 while ( i < size )
00133 {
00134 count = 1;
00135 s >> byte;
00136 if ( byte > 0xc0 )
00137 {
00138 count = byte - 0xc0;
00139 s >> byte;
00140 }
00141 while ( count-- && i < size )
00142 buf[ i++ ] = byte;
00143 }
00144 }
00145 else
00146 {
00147
00148 while ( i < size )
00149 {
00150 s >> byte;
00151 buf[ i++ ] = byte;
00152 }
00153 }
00154 }
00155
00156 static void readImage1( QImage &img, QDataStream &s, const PCXHEADER &header )
00157 {
00158 QByteArray buf( header.BytesPerLine, 0 );
00159
00160 img = QImage( header.width(), header.height(), QImage::Format_Mono );
00161 img.setNumColors( 2 );
00162
00163 for ( int y=0; y<header.height(); ++y )
00164 {
00165 if ( s.atEnd() )
00166 {
00167 img = QImage();
00168 return;
00169 }
00170
00171 readLine( s, buf, header );
00172 uchar *p = img.scanLine( y );
00173 unsigned int bpl = qMin((quint16)((header.width()+7)/8), header.BytesPerLine);
00174 for ( unsigned int x=0; x< bpl; ++x )
00175 p[ x ] = buf[x];
00176 }
00177
00178
00179 img.setColor( 0, qRgb( 0, 0, 0 ) );
00180 img.setColor( 1, qRgb( 255, 255, 255 ) );
00181 }
00182
00183 static void readImage4( QImage &img, QDataStream &s, const PCXHEADER &header )
00184 {
00185 QByteArray buf( header.BytesPerLine*4, 0 );
00186 QByteArray pixbuf( header.width(), 0 );
00187
00188 img = QImage( header.width(), header.height(), QImage::Format_Indexed8 );
00189 img.setNumColors( 16 );
00190
00191 for ( int y=0; y<header.height(); ++y )
00192 {
00193 if ( s.atEnd() )
00194 {
00195 img = QImage();
00196 return;
00197 }
00198
00199 pixbuf.fill( 0 );
00200 readLine( s, buf, header );
00201
00202 for ( int i=0; i<4; i++ )
00203 {
00204 quint32 offset = i*header.BytesPerLine;
00205 for ( int x=0; x<header.width(); ++x )
00206 if ( buf[ offset + ( x/8 ) ] & ( 128 >> ( x%8 ) ) )
00207 pixbuf[ x ] = (int)(pixbuf[ x ]) + ( 1 << i );
00208 }
00209
00210 uchar *p = img.scanLine( y );
00211 for ( int x=0; x<header.width(); ++x )
00212 p[ x ] = pixbuf[ x ];
00213 }
00214
00215
00216 for ( int i=0; i<16; ++i )
00217 img.setColor( i, header.ColorMap.color( i ) );
00218 }
00219
00220 static void readImage8( QImage &img, QDataStream &s, const PCXHEADER &header )
00221 {
00222 QByteArray buf( header.BytesPerLine, 0 );
00223
00224 img = QImage( header.width(), header.height(), QImage::Format_Indexed8 );
00225 img.setNumColors( 256 );
00226
00227 for ( int y=0; y<header.height(); ++y )
00228 {
00229 if ( s.atEnd() )
00230 {
00231 img = QImage();
00232 return;
00233 }
00234
00235 readLine( s, buf, header );
00236
00237 uchar *p = img.scanLine( y );
00238 unsigned int bpl = qMin(header.BytesPerLine, (quint16)header.width());
00239 for ( unsigned int x=0; x<bpl; ++x )
00240 p[ x ] = buf[ x ];
00241 }
00242
00243 quint8 flag;
00244 s >> flag;
00245 kDebug( 399 ) << "Palette Flag: " << flag;
00246
00247 if ( flag == 12 && ( header.Version == 5 || header.Version == 2 ) )
00248 {
00249
00250 quint8 r, g, b;
00251 for ( int i=0; i<256; ++i )
00252 {
00253 s >> r >> g >> b;
00254 img.setColor( i, qRgb( r, g, b ) );
00255 }
00256 }
00257 }
00258
00259 static void readImage24( QImage &img, QDataStream &s, const PCXHEADER &header )
00260 {
00261 QByteArray r_buf( header.BytesPerLine, 0 );
00262 QByteArray g_buf( header.BytesPerLine, 0 );
00263 QByteArray b_buf( header.BytesPerLine, 0 );
00264
00265 img = QImage( header.width(), header.height(), QImage::Format_RGB32 );
00266
00267 for ( int y=0; y<header.height(); ++y )
00268 {
00269 if ( s.atEnd() )
00270 {
00271 img = QImage();
00272 return;
00273 }
00274
00275 readLine( s, r_buf, header );
00276 readLine( s, g_buf, header );
00277 readLine( s, b_buf, header );
00278
00279 uint *p = ( uint * )img.scanLine( y );
00280 for ( int x=0; x<header.width(); ++x )
00281 p[ x ] = qRgb( r_buf[ x ], g_buf[ x ], b_buf[ x ] );
00282 }
00283 }
00284
00285 static void writeLine( QDataStream &s, QByteArray &buf )
00286 {
00287 quint32 i = 0;
00288 quint32 size = buf.size();
00289 quint8 count, data;
00290 char byte;
00291
00292 while ( i < size )
00293 {
00294 count = 1;
00295 byte = buf[ i++ ];
00296
00297 while ( ( i < size ) && ( byte == buf[ i ] ) && ( count < 63 ) )
00298 {
00299 ++i;
00300 ++count;
00301 }
00302
00303 data = byte;
00304
00305 if ( count > 1 || data >= 0xc0 )
00306 {
00307 count |= 0xc0;
00308 s << count;
00309 }
00310
00311 s << data;
00312 }
00313 }
00314
00315 static void writeImage1( QImage &img, QDataStream &s, PCXHEADER &header )
00316 {
00317 img = img.convertToFormat( QImage::Format_Mono );
00318
00319 header.Bpp = 1;
00320 header.NPlanes = 1;
00321 header.BytesPerLine = img.bytesPerLine();
00322
00323 s << header;
00324
00325 QByteArray buf( header.BytesPerLine, 0 );
00326
00327 for ( int y=0; y<header.height(); ++y )
00328 {
00329 quint8 *p = img.scanLine( y );
00330
00331
00332 for ( int i=0; i<header.BytesPerLine; ++i )
00333 buf[ i ] = ~p[ i ];
00334
00335 writeLine( s, buf );
00336 }
00337 }
00338
00339 static void writeImage4( QImage &img, QDataStream &s, PCXHEADER &header )
00340 {
00341 header.Bpp = 1;
00342 header.NPlanes = 4;
00343 header.BytesPerLine = header.width()/8;
00344
00345 for ( int i=0; i<16; ++i )
00346 header.ColorMap.setColor( i, img.color( i ) );
00347
00348 s << header;
00349
00350 QByteArray buf[ 4 ];
00351
00352 for ( int i=0; i<4; ++i )
00353 buf[ i ].resize( header.BytesPerLine );
00354
00355 for ( int y=0; y<header.height(); ++y )
00356 {
00357 quint8 *p = img.scanLine( y );
00358
00359 for ( int i=0; i<4; ++i )
00360 buf[ i ].fill( 0 );
00361
00362 for ( int x=0; x<header.width(); ++x )
00363 {
00364 for ( int i=0; i<4; ++i )
00365 if ( *( p+x ) & ( 1 << i ) )
00366 buf[ i ][ x/8 ] = (int)(buf[ i ][ x/8 ])| 1 << ( 7-x%8 );
00367 }
00368
00369 for ( int i=0; i<4; ++i )
00370 writeLine( s, buf[ i ] );
00371 }
00372 }
00373
00374 static void writeImage8( QImage &img, QDataStream &s, PCXHEADER &header )
00375 {
00376 header.Bpp = 8;
00377 header.NPlanes = 1;
00378 header.BytesPerLine = img.bytesPerLine();
00379
00380 s << header;
00381
00382 QByteArray buf( header.BytesPerLine, 0 );
00383
00384 for ( int y=0; y<header.height(); ++y )
00385 {
00386 quint8 *p = img.scanLine( y );
00387
00388 for ( int i=0; i<header.BytesPerLine; ++i )
00389 buf[ i ] = p[ i ];
00390
00391 writeLine( s, buf );
00392 }
00393
00394
00395 quint8 byte = 12;
00396 s << byte;
00397
00398
00399 for ( int i=0; i<256; ++i )
00400 s << RGB::from( img.color( i ) );
00401 }
00402
00403 static void writeImage24( QImage &img, QDataStream &s, PCXHEADER &header )
00404 {
00405 header.Bpp = 8;
00406 header.NPlanes = 3;
00407 header.BytesPerLine = header.width();
00408
00409 s << header;
00410
00411 QByteArray r_buf( header.width(), 0 );
00412 QByteArray g_buf( header.width(), 0 );
00413 QByteArray b_buf( header.width(), 0 );
00414
00415 for ( int y=0; y<header.height(); ++y )
00416 {
00417 uint *p = ( uint * )img.scanLine( y );
00418
00419 for ( int x=0; x<header.width(); ++x )
00420 {
00421 QRgb rgb = *p++;
00422 r_buf[ x ] = qRed( rgb );
00423 g_buf[ x ] = qGreen( rgb );
00424 b_buf[ x ] = qBlue( rgb );
00425 }
00426
00427 writeLine( s, r_buf );
00428 writeLine( s, g_buf );
00429 writeLine( s, b_buf );
00430 }
00431 }
00432
00433
00434 PCXHandler::PCXHandler()
00435 {
00436 }
00437
00438 bool PCXHandler::canRead() const
00439 {
00440 if (canRead(device()))
00441 {
00442 setFormat("pcx");
00443 return true;
00444 }
00445 return false;
00446 }
00447
00448 bool PCXHandler::read(QImage *outImage)
00449 {
00450 QDataStream s( device() );
00451 s.setByteOrder( QDataStream::LittleEndian );
00452
00453 if ( s.device()->size() < 128 )
00454 {
00455 return false;
00456 }
00457
00458 PCXHEADER header;
00459
00460 s >> header;
00461
00462 if ( header.Manufacturer != 10 || s.atEnd())
00463 {
00464 return false;
00465 }
00466
00467 int w = header.width();
00468 int h = header.height();
00469
00470 kDebug( 399 ) << "Manufacturer: " << header.Manufacturer;
00471 kDebug( 399 ) << "Version: " << header.Version;
00472 kDebug( 399 ) << "Encoding: " << header.Encoding;
00473 kDebug( 399 ) << "Bpp: " << header.Bpp;
00474 kDebug( 399 ) << "Width: " << w;
00475 kDebug( 399 ) << "Height: " << h;
00476 kDebug( 399 ) << "Window: " << header.XMin << "," << header.XMax << ","
00477 << header.YMin << "," << header.YMax << endl;
00478 kDebug( 399 ) << "BytesPerLine: " << header.BytesPerLine;
00479 kDebug( 399 ) << "NPlanes: " << header.NPlanes;
00480
00481 QImage img;
00482
00483 if ( header.Bpp == 1 && header.NPlanes == 1 )
00484 {
00485 readImage1( img, s, header );
00486 }
00487 else if ( header.Bpp == 1 && header.NPlanes == 4 )
00488 {
00489 readImage4( img, s, header );
00490 }
00491 else if ( header.Bpp == 8 && header.NPlanes == 1 )
00492 {
00493 readImage8( img, s, header );
00494 }
00495 else if ( header.Bpp == 8 && header.NPlanes == 3 )
00496 {
00497 readImage24( img, s, header );
00498 }
00499
00500 kDebug( 399 ) << "Image Bytes: " << img.numBytes();
00501 kDebug( 399 ) << "Image Bytes Per Line: " << img.bytesPerLine();
00502 kDebug( 399 ) << "Image Depth: " << img.depth();
00503
00504 if ( !img.isNull() )
00505 {
00506 *outImage = img;
00507 return true;
00508 }
00509 else
00510 {
00511 return false;
00512 }
00513 }
00514
00515 bool PCXHandler::write(const QImage &image)
00516 {
00517 QDataStream s( device() );
00518 s.setByteOrder( QDataStream::LittleEndian );
00519
00520 QImage img = image;
00521
00522 int w = img.width();
00523 int h = img.height();
00524
00525 kDebug( 399 ) << "Width: " << w;
00526 kDebug( 399 ) << "Height: " << h;
00527 kDebug( 399 ) << "Depth: " << img.depth();
00528 kDebug( 399 ) << "BytesPerLine: " << img.bytesPerLine();
00529 kDebug( 399 ) << "Num Colors: " << img.numColors();
00530
00531 PCXHEADER header;
00532
00533 header.Manufacturer = 10;
00534 header.Version = 5;
00535 header.Encoding = 1;
00536 header.XMin = 0;
00537 header.YMin = 0;
00538 header.XMax = w-1;
00539 header.YMax = h-1;
00540 header.HDpi = 300;
00541 header.YDpi = 300;
00542 header.Reserved = 0;
00543 header.PaletteInfo =1;
00544
00545 if ( img.depth() == 1 )
00546 {
00547 writeImage1( img, s, header );
00548 }
00549 else if ( img.depth() == 8 && img.numColors() <= 16 )
00550 {
00551 writeImage4( img, s, header );
00552 }
00553 else if ( img.depth() == 8 )
00554 {
00555 writeImage8( img, s, header );
00556 }
00557 else if ( img.depth() == 32 )
00558 {
00559 writeImage24( img, s, header );
00560 }
00561
00562 return true;
00563 }
00564
00565 QByteArray PCXHandler::name() const
00566 {
00567 return "pcx";
00568 }
00569
00570 bool PCXHandler::canRead(QIODevice *device)
00571 {
00572 if (!device) {
00573 qWarning("PCXHandler::canRead() called with no device");
00574 return false;
00575 }
00576
00577 qint64 oldPos = device->pos();
00578
00579 char head[1];
00580 qint64 readBytes = device->read(head, sizeof(head));
00581 if (readBytes != sizeof(head)) {
00582 if (device->isSequential()) {
00583 while (readBytes > 0)
00584 device->ungetChar(head[readBytes-- - 1]);
00585 } else {
00586 device->seek(oldPos);
00587 }
00588 return false;
00589 }
00590
00591 if (device->isSequential()) {
00592 while (readBytes > 0)
00593 device->ungetChar(head[readBytes-- - 1]);
00594 } else {
00595 device->seek(oldPos);
00596 }
00597
00598 return qstrncmp(head, "\012", 1) == 0;
00599 }
00600
00601 class PCXPlugin : public QImageIOPlugin
00602 {
00603 public:
00604 QStringList keys() const;
00605 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
00606 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
00607 };
00608
00609 QStringList PCXPlugin::keys() const
00610 {
00611 return QStringList() << "pcx" << "PCX";
00612 }
00613
00614 QImageIOPlugin::Capabilities PCXPlugin::capabilities(QIODevice *device, const QByteArray &format) const
00615 {
00616 if (format == "pcx" || format == "PCX")
00617 return Capabilities(CanRead | CanWrite);
00618 if (!format.isEmpty())
00619 return 0;
00620 if (!device->isOpen())
00621 return 0;
00622
00623 Capabilities cap;
00624 if (device->isReadable() && PCXHandler::canRead(device))
00625 cap |= CanRead;
00626 if (device->isWritable())
00627 cap |= CanWrite;
00628 return cap;
00629 }
00630
00631 QImageIOHandler *PCXPlugin::create(QIODevice *device, const QByteArray &format) const
00632 {
00633 QImageIOHandler *handler = new PCXHandler;
00634 handler->setDevice(device);
00635 handler->setFormat(format);
00636 return handler;
00637 }
00638
00639 Q_EXPORT_STATIC_PLUGIN(PCXPlugin)
00640 Q_EXPORT_PLUGIN2(pcx, PCXPlugin)
00641
00642
00643
00644