KImgIO
pcx.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2002-2005 Nadeem Hasan <nhasan@kde.org> 00003 00004 This program is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Lesser General Public 00006 License (LGPL) as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 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 // Skip the rest of the header 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 // Initialize all data to zero 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 // Uncompress the image data 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 // Image is not compressed (possible?) 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 // Set the color palette 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 // Read the palette 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 // Read the palette 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 // Invert as QImage uses reverse palette for monochrome images? 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 // Write palette flag 00395 quint8 byte = 12; 00396 s << byte; 00397 00398 // Write palette 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 /* vim: et sw=2 ts=2 00643 */ 00644
KDE 4.6 API Reference