pcx.cpp
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 <qimage.h> 00013 00014 #include <kdebug.h> 00015 00016 static QDataStream &operator>>( QDataStream &s, RGB &rgb ) 00017 { 00018 s >> rgb.r >> rgb.g >> rgb.b; 00019 00020 return s; 00021 } 00022 00023 static QDataStream &operator>>( QDataStream &s, Palette &pal ) 00024 { 00025 for ( int i=0; i<16; ++i ) 00026 s >> pal.rgb[ i ]; 00027 00028 return s; 00029 } 00030 00031 static QDataStream &operator>>( QDataStream &s, PCXHEADER &ph ) 00032 { 00033 s >> ph.Manufacturer; 00034 s >> ph.Version; 00035 s >> ph.Encoding; 00036 s >> ph.Bpp; 00037 s >> ph.XMin >> ph.YMin >> ph.XMax >> ph.YMax; 00038 s >> ph.HDpi >> ph.YDpi; 00039 s >> ph.ColorMap; 00040 s >> ph.Reserved; 00041 s >> ph.NPlanes; 00042 s >> ph.BytesPerLine; 00043 s >> ph.PaletteInfo; 00044 s >> ph.HScreenSize; 00045 s >> ph.VScreenSize; 00046 00047 // Skip the rest of the header 00048 Q_UINT8 byte; 00049 while ( s.device()->at() < 128 ) 00050 s >> byte; 00051 00052 return s; 00053 } 00054 00055 static QDataStream &operator<<( QDataStream &s, const RGB &rgb ) 00056 { 00057 s << rgb.r << rgb.g << rgb.b; 00058 00059 return s; 00060 } 00061 00062 static QDataStream &operator<<( QDataStream &s, const Palette &pal ) 00063 { 00064 for ( int i=0; i<16; ++i ) 00065 s << pal.rgb[ i ]; 00066 00067 return s; 00068 } 00069 00070 static QDataStream &operator<<( QDataStream &s, const PCXHEADER &ph ) 00071 { 00072 s << ph.Manufacturer; 00073 s << ph.Version; 00074 s << ph.Encoding; 00075 s << ph.Bpp; 00076 s << ph.XMin << ph.YMin << ph.XMax << ph.YMax; 00077 s << ph.HDpi << ph.YDpi; 00078 s << ph.ColorMap; 00079 s << ph.Reserved; 00080 s << ph.NPlanes; 00081 s << ph.BytesPerLine; 00082 s << ph.PaletteInfo; 00083 s << ph.HScreenSize; 00084 s << ph.VScreenSize; 00085 00086 Q_UINT8 byte = 0; 00087 for ( int i=0; i<54; ++i ) 00088 s << byte; 00089 00090 return s; 00091 } 00092 00093 PCXHEADER::PCXHEADER() 00094 { 00095 // Initialize all data to zero 00096 QByteArray dummy( 128 ); 00097 dummy.fill( 0 ); 00098 QDataStream s( dummy, IO_ReadOnly ); 00099 s >> *this; 00100 } 00101 00102 static void readLine( QDataStream &s, QByteArray &buf, const PCXHEADER &header ) 00103 { 00104 Q_UINT32 i=0; 00105 Q_UINT32 size = buf.size(); 00106 Q_UINT8 byte, count; 00107 00108 if ( header.isCompressed() ) 00109 { 00110 // Uncompress the image data 00111 while ( i < size ) 00112 { 00113 count = 1; 00114 s >> byte; 00115 if ( byte > 0xc0 ) 00116 { 00117 count = byte - 0xc0; 00118 s >> byte; 00119 } 00120 while ( count-- && i < size ) 00121 buf[ i++ ] = byte; 00122 } 00123 } 00124 else 00125 { 00126 // Image is not compressed (possible?) 00127 while ( i < size ) 00128 { 00129 s >> byte; 00130 buf[ i++ ] = byte; 00131 } 00132 } 00133 } 00134 00135 static void readImage1( QImage &img, QDataStream &s, const PCXHEADER &header ) 00136 { 00137 QByteArray buf( header.BytesPerLine ); 00138 00139 if(!img.create( header.width(), header.height(), 1, 2, QImage::BigEndian )) 00140 return; 00141 00142 for ( int y=0; y<header.height(); ++y ) 00143 { 00144 if ( s.atEnd() ) 00145 { 00146 img.reset(); 00147 return; 00148 } 00149 00150 readLine( s, buf, header ); 00151 uchar *p = img.scanLine( y ); 00152 unsigned int bpl = QMIN((header.width()+7)/8, header.BytesPerLine); 00153 for ( unsigned int x=0; x< bpl; ++x ) 00154 p[ x ] = buf[x]; 00155 } 00156 00157 // Set the color palette 00158 img.setColor( 0, qRgb( 0, 0, 0 ) ); 00159 img.setColor( 1, qRgb( 255, 255, 255 ) ); 00160 } 00161 00162 static void readImage4( QImage &img, QDataStream &s, const PCXHEADER &header ) 00163 { 00164 QByteArray buf( header.BytesPerLine*4 ); 00165 QByteArray pixbuf( header.width() ); 00166 00167 if(!img.create( header.width(), header.height(), 8, 16 )) 00168 return; 00169 00170 for ( int y=0; y<header.height(); ++y ) 00171 { 00172 if ( s.atEnd() ) 00173 { 00174 img.reset(); 00175 return; 00176 } 00177 00178 pixbuf.fill( 0 ); 00179 readLine( s, buf, header ); 00180 00181 for ( int i=0; i<4; i++ ) 00182 { 00183 Q_UINT32 offset = i*header.BytesPerLine; 00184 for ( unsigned int x=0; x<header.width(); ++x ) 00185 if ( buf[ offset + ( x/8 ) ] & ( 128 >> ( x%8 ) ) ) 00186 pixbuf[ x ] += ( 1 << i ); 00187 } 00188 00189 uchar *p = img.scanLine( y ); 00190 for ( unsigned int x=0; x<header.width(); ++x ) 00191 p[ x ] = pixbuf[ x ]; 00192 } 00193 00194 // Read the palette 00195 for ( int i=0; i<16; ++i ) 00196 img.setColor( i, header.ColorMap.color( i ) ); 00197 } 00198 00199 static void readImage8( QImage &img, QDataStream &s, const PCXHEADER &header ) 00200 { 00201 QByteArray buf( header.BytesPerLine ); 00202 00203 if(!img.create( header.width(), header.height(), 8, 256 )) 00204 return; 00205 00206 for ( int y=0; y<header.height(); ++y ) 00207 { 00208 if ( s.atEnd() ) 00209 { 00210 img.reset(); 00211 return; 00212 } 00213 00214 readLine( s, buf, header ); 00215 00216 uchar *p = img.scanLine( y ); 00217 unsigned int bpl = QMIN(header.BytesPerLine, header.width()); 00218 for ( unsigned int x=0; x<bpl; ++x ) 00219 p[ x ] = buf[ x ]; 00220 } 00221 00222 Q_UINT8 flag; 00223 s >> flag; 00224 kdDebug( 399 ) << "Palette Flag: " << flag << endl; 00225 00226 if ( flag == 12 && ( header.Version == 5 || header.Version == 2 ) ) 00227 { 00228 // Read the palette 00229 Q_UINT8 r, g, b; 00230 for ( int i=0; i<256; ++i ) 00231 { 00232 s >> r >> g >> b; 00233 img.setColor( i, qRgb( r, g, b ) ); 00234 } 00235 } 00236 } 00237 00238 static void readImage24( QImage &img, QDataStream &s, const PCXHEADER &header ) 00239 { 00240 QByteArray r_buf( header.BytesPerLine ); 00241 QByteArray g_buf( header.BytesPerLine ); 00242 QByteArray b_buf( header.BytesPerLine ); 00243 00244 if(!img.create( header.width(), header.height(), 32 )) 00245 return; 00246 00247 for ( int y=0; y<header.height(); ++y ) 00248 { 00249 if ( s.atEnd() ) 00250 { 00251 img.reset(); 00252 return; 00253 } 00254 00255 readLine( s, r_buf, header ); 00256 readLine( s, g_buf, header ); 00257 readLine( s, b_buf, header ); 00258 00259 uint *p = ( uint * )img.scanLine( y ); 00260 for ( unsigned int x=0; x<header.width(); ++x ) 00261 p[ x ] = qRgb( r_buf[ x ], g_buf[ x ], b_buf[ x ] ); 00262 } 00263 } 00264 00265 KDE_EXPORT void kimgio_pcx_read( QImageIO *io ) 00266 { 00267 QDataStream s( io->ioDevice() ); 00268 s.setByteOrder( QDataStream::LittleEndian ); 00269 00270 if ( s.device()->size() < 128 ) 00271 { 00272 io->setStatus( -1 ); 00273 return; 00274 } 00275 00276 PCXHEADER header; 00277 00278 s >> header; 00279 00280 if ( header.Manufacturer != 10 || s.atEnd()) 00281 { 00282 io->setStatus( -1 ); 00283 return; 00284 } 00285 00286 int w = header.width(); 00287 int h = header.height(); 00288 00289 kdDebug( 399 ) << "Manufacturer: " << header.Manufacturer << endl; 00290 kdDebug( 399 ) << "Version: " << header.Version << endl; 00291 kdDebug( 399 ) << "Encoding: " << header.Encoding << endl; 00292 kdDebug( 399 ) << "Bpp: " << header.Bpp << endl; 00293 kdDebug( 399 ) << "Width: " << w << endl; 00294 kdDebug( 399 ) << "Height: " << h << endl; 00295 kdDebug( 399 ) << "Window: " << header.XMin << "," << header.XMax << "," 00296 << header.YMin << "," << header.YMax << endl; 00297 kdDebug( 399 ) << "BytesPerLine: " << header.BytesPerLine << endl; 00298 kdDebug( 399 ) << "NPlanes: " << header.NPlanes << endl; 00299 00300 QImage img; 00301 00302 if ( header.Bpp == 1 && header.NPlanes == 1 ) 00303 { 00304 readImage1( img, s, header ); 00305 } 00306 else if ( header.Bpp == 1 && header.NPlanes == 4 ) 00307 { 00308 readImage4( img, s, header ); 00309 } 00310 else if ( header.Bpp == 8 && header.NPlanes == 1 ) 00311 { 00312 readImage8( img, s, header ); 00313 } 00314 else if ( header.Bpp == 8 && header.NPlanes == 3 ) 00315 { 00316 readImage24( img, s, header ); 00317 } 00318 00319 kdDebug( 399 ) << "Image Bytes: " << img.numBytes() << endl; 00320 kdDebug( 399 ) << "Image Bytes Per Line: " << img.bytesPerLine() << endl; 00321 kdDebug( 399 ) << "Image Depth: " << img.depth() << endl; 00322 00323 if ( !img.isNull() ) 00324 { 00325 io->setImage( img ); 00326 io->setStatus( 0 ); 00327 } 00328 else 00329 { 00330 io->setStatus( -1 ); 00331 } 00332 } 00333 00334 static void writeLine( QDataStream &s, QByteArray &buf ) 00335 { 00336 Q_UINT32 i = 0; 00337 Q_UINT32 size = buf.size(); 00338 Q_UINT8 count, data; 00339 char byte; 00340 00341 while ( i < size ) 00342 { 00343 count = 1; 00344 byte = buf[ i++ ]; 00345 00346 while ( ( i < size ) && ( byte == buf[ i ] ) && ( count < 63 ) ) 00347 { 00348 ++i; 00349 ++count; 00350 } 00351 00352 data = byte; 00353 00354 if ( count > 1 || data >= 0xc0 ) 00355 { 00356 count |= 0xc0; 00357 s << count; 00358 } 00359 00360 s << data; 00361 } 00362 } 00363 00364 static void writeImage1( QImage &img, QDataStream &s, PCXHEADER &header ) 00365 { 00366 img = img.convertBitOrder( QImage::BigEndian ); 00367 00368 header.Bpp = 1; 00369 header.NPlanes = 1; 00370 header.BytesPerLine = img.bytesPerLine(); 00371 00372 s << header; 00373 00374 QByteArray buf( header.BytesPerLine ); 00375 00376 for ( int y=0; y<header.height(); ++y ) 00377 { 00378 Q_UINT8 *p = img.scanLine( y ); 00379 00380 // Invert as QImage uses reverse palette for monochrome images? 00381 for ( int i=0; i<header.BytesPerLine; ++i ) 00382 buf[ i ] = ~p[ i ]; 00383 00384 writeLine( s, buf ); 00385 } 00386 } 00387 00388 static void writeImage4( QImage &img, QDataStream &s, PCXHEADER &header ) 00389 { 00390 header.Bpp = 1; 00391 header.NPlanes = 4; 00392 header.BytesPerLine = header.width()/8; 00393 00394 for ( int i=0; i<16; ++i ) 00395 header.ColorMap.setColor( i, img.color( i ) ); 00396 00397 s << header; 00398 00399 QByteArray buf[ 4 ]; 00400 00401 for ( int i=0; i<4; ++i ) 00402 buf[ i ].resize( header.BytesPerLine ); 00403 00404 for ( int y=0; y<header.height(); ++y ) 00405 { 00406 Q_UINT8 *p = img.scanLine( y ); 00407 00408 for ( int i=0; i<4; ++i ) 00409 buf[ i ].fill( 0 ); 00410 00411 for ( unsigned int x=0; x<header.width(); ++x ) 00412 { 00413 for ( int i=0; i<4; ++i ) 00414 if ( *( p+x ) & ( 1 << i ) ) 00415 buf[ i ][ x/8 ] |= 1 << ( 7-x%8 ); 00416 } 00417 00418 for ( int i=0; i<4; ++i ) 00419 writeLine( s, buf[ i ] ); 00420 } 00421 } 00422 00423 static void writeImage8( QImage &img, QDataStream &s, PCXHEADER &header ) 00424 { 00425 header.Bpp = 8; 00426 header.NPlanes = 1; 00427 header.BytesPerLine = img.bytesPerLine(); 00428 00429 s << header; 00430 00431 QByteArray buf( header.BytesPerLine ); 00432 00433 for ( int y=0; y<header.height(); ++y ) 00434 { 00435 Q_UINT8 *p = img.scanLine( y ); 00436 00437 for ( int i=0; i<header.BytesPerLine; ++i ) 00438 buf[ i ] = p[ i ]; 00439 00440 writeLine( s, buf ); 00441 } 00442 00443 // Write palette flag 00444 Q_UINT8 byte = 12; 00445 s << byte; 00446 00447 // Write palette 00448 for ( int i=0; i<256; ++i ) 00449 s << RGB( img.color( i ) ); 00450 } 00451 00452 static void writeImage24( QImage &img, QDataStream &s, PCXHEADER &header ) 00453 { 00454 header.Bpp = 8; 00455 header.NPlanes = 3; 00456 header.BytesPerLine = header.width(); 00457 00458 s << header; 00459 00460 QByteArray r_buf( header.width() ); 00461 QByteArray g_buf( header.width() ); 00462 QByteArray b_buf( header.width() ); 00463 00464 for ( int y=0; y<header.height(); ++y ) 00465 { 00466 uint *p = ( uint * )img.scanLine( y ); 00467 00468 for ( unsigned int x=0; x<header.width(); ++x ) 00469 { 00470 QRgb rgb = *p++; 00471 r_buf[ x ] = qRed( rgb ); 00472 g_buf[ x ] = qGreen( rgb ); 00473 b_buf[ x ] = qBlue( rgb ); 00474 } 00475 00476 writeLine( s, r_buf ); 00477 writeLine( s, g_buf ); 00478 writeLine( s, b_buf ); 00479 } 00480 } 00481 00482 KDE_EXPORT void kimgio_pcx_write( QImageIO *io ) 00483 { 00484 QDataStream s( io->ioDevice() ); 00485 s.setByteOrder( QDataStream::LittleEndian ); 00486 00487 QImage img = io->image(); 00488 00489 int w = img.width(); 00490 int h = img.height(); 00491 00492 kdDebug( 399 ) << "Width: " << w << endl; 00493 kdDebug( 399 ) << "Height: " << h << endl; 00494 kdDebug( 399 ) << "Depth: " << img.depth() << endl; 00495 kdDebug( 399 ) << "BytesPerLine: " << img.bytesPerLine() << endl; 00496 kdDebug( 399 ) << "Num Colors: " << img.numColors() << endl; 00497 00498 PCXHEADER header; 00499 00500 header.Manufacturer = 10; 00501 header.Version = 5; 00502 header.Encoding = 1; 00503 header.XMin = 0; 00504 header.YMin = 0; 00505 header.XMax = w-1; 00506 header.YMax = h-1; 00507 header.HDpi = 300; 00508 header.YDpi = 300; 00509 header.Reserved = 0; 00510 header.PaletteInfo =1; 00511 00512 if ( img.depth() == 1 ) 00513 { 00514 writeImage1( img, s, header ); 00515 } 00516 else if ( img.depth() == 8 && img.numColors() <= 16 ) 00517 { 00518 writeImage4( img, s, header ); 00519 } 00520 else if ( img.depth() == 8 ) 00521 { 00522 writeImage8( img, s, header ); 00523 } 00524 else if ( img.depth() == 32 ) 00525 { 00526 writeImage24( img, s, header ); 00527 } 00528 00529 io->setStatus( 0 ); 00530 } 00531 00532 /* vim: et sw=2 ts=2 00533 */ 00534