/* This file is part of the KDE project Copyright (C) 2002-2005 Nadeem Hasan This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "pcx.h" #include #include static QDataStream &operator>>( QDataStream &s, RGB &rgb ) { quint8 r, g, b; s >> r >> g >> b; rgb.r = r; rgb.g = g; rgb.b = b; return s; } static QDataStream &operator>>( QDataStream &s, Palette &pal ) { for ( int i=0; i<16; ++i ) s >> pal.rgb[ i ]; return s; } static QDataStream &operator>>( QDataStream &s, PCXHEADER &ph ) { quint8 m, ver, enc, bpp; s >> m >> ver >> enc >> bpp; ph.Manufacturer = m; ph.Version = ver; ph.Encoding = enc; ph.Bpp = bpp; quint16 xmin, ymin, xmax, ymax; s >> xmin >> ymin >> xmax >> ymax; ph.XMin = xmin; ph.YMin = ymin; ph.XMax = xmax; ph.YMax = ymax; quint16 hdpi, ydpi; s >> hdpi >> ydpi; ph.HDpi = hdpi; ph.YDpi = ydpi; Palette colorMap; quint8 res, np; s >> colorMap >> res >> np; ph.ColorMap = colorMap; ph.Reserved = res; ph.NPlanes = np; quint16 bytesperline; s >> bytesperline; ph.BytesPerLine = bytesperline; quint16 paletteinfo; s >> paletteinfo; ph.PaletteInfo = paletteinfo; quint16 hscreensize, vscreensize; s >> hscreensize; ph.HScreenSize = hscreensize; s >> vscreensize; ph.VScreenSize = vscreensize; // Skip the rest of the header quint8 byte; while ( s.device()->pos() < 128 ) s >> byte; return s; } static QDataStream &operator<<( QDataStream &s, const RGB &rgb ) { s << rgb.r << rgb.g << rgb.b; return s; } static QDataStream &operator<<( QDataStream &s, const Palette &pal ) { for ( int i=0; i<16; ++i ) s << pal.rgb[ i ]; return s; } static QDataStream &operator<<( QDataStream &s, const PCXHEADER &ph ) { s << ph.Manufacturer; s << ph.Version; s << ph.Encoding; s << ph.Bpp; s << ph.XMin << ph.YMin << ph.XMax << ph.YMax; s << ph.HDpi << ph.YDpi; s << ph.ColorMap; s << ph.Reserved; s << ph.NPlanes; s << ph.BytesPerLine; s << ph.PaletteInfo; s << ph.HScreenSize; s << ph.VScreenSize; quint8 byte = 0; for ( int i=0; i<54; ++i ) s << byte; return s; } PCXHEADER::PCXHEADER() { // Initialize all data to zero QByteArray dummy( 128, 0 ); dummy.fill( 0 ); QDataStream s( &dummy, QIODevice::ReadOnly ); s >> *this; } static void readLine( QDataStream &s, QByteArray &buf, const PCXHEADER &header ) { quint32 i=0; quint32 size = buf.size(); quint8 byte, count; if ( header.isCompressed() ) { // Uncompress the image data while ( i < size ) { count = 1; s >> byte; if ( byte > 0xc0 ) { count = byte - 0xc0; s >> byte; } while ( count-- && i < size ) buf[ i++ ] = byte; } } else { // Image is not compressed (possible?) while ( i < size ) { s >> byte; buf[ i++ ] = byte; } } } static void readImage1( QImage &img, QDataStream &s, const PCXHEADER &header ) { QByteArray buf( header.BytesPerLine, 0 ); img = QImage( header.width(), header.height(), QImage::Format_Mono ); img.setColorCount( 2 ); for ( int y=0; y> ( x%8 ) ) ) pixbuf[ x ] = (int)(pixbuf[ x ]) + ( 1 << i ); } uchar *p = img.scanLine( y ); for ( int x=0; x> flag; kDebug( 399 ) << "Palette Flag: " << flag; if ( flag == 12 && ( header.Version == 5 || header.Version == 2 ) ) { // Read the palette quint8 r, g, b; for ( int i=0; i<256; ++i ) { s >> r >> g >> b; img.setColor( i, qRgb( r, g, b ) ); } } } static void readImage24( QImage &img, QDataStream &s, const PCXHEADER &header ) { QByteArray r_buf( header.BytesPerLine, 0 ); QByteArray g_buf( header.BytesPerLine, 0 ); QByteArray b_buf( header.BytesPerLine, 0 ); img = QImage( header.width(), header.height(), QImage::Format_RGB32 ); for ( int y=0; y 1 || data >= 0xc0 ) { count |= 0xc0; s << count; } s << data; } } static void writeImage1( QImage &img, QDataStream &s, PCXHEADER &header ) { img = img.convertToFormat( QImage::Format_Mono ); header.Bpp = 1; header.NPlanes = 1; header.BytesPerLine = img.bytesPerLine(); s << header; QByteArray buf( header.BytesPerLine, 0 ); for ( int y=0; ysize() < 128 ) { return false; } PCXHEADER header; s >> header; if ( header.Manufacturer != 10 || s.atEnd()) { return false; } int w = header.width(); int h = header.height(); kDebug( 399 ) << "Manufacturer: " << header.Manufacturer; kDebug( 399 ) << "Version: " << header.Version; kDebug( 399 ) << "Encoding: " << header.Encoding; kDebug( 399 ) << "Bpp: " << header.Bpp; kDebug( 399 ) << "Width: " << w; kDebug( 399 ) << "Height: " << h; kDebug( 399 ) << "Window: " << header.XMin << "," << header.XMax << "," << header.YMin << "," << header.YMax << endl; kDebug( 399 ) << "BytesPerLine: " << header.BytesPerLine; kDebug( 399 ) << "NPlanes: " << header.NPlanes; QImage img; if ( header.Bpp == 1 && header.NPlanes == 1 ) { readImage1( img, s, header ); } else if ( header.Bpp == 1 && header.NPlanes == 4 ) { readImage4( img, s, header ); } else if ( header.Bpp == 8 && header.NPlanes == 1 ) { readImage8( img, s, header ); } else if ( header.Bpp == 8 && header.NPlanes == 3 ) { readImage24( img, s, header ); } kDebug( 399 ) << "Image Bytes: " << img.byteCount(); kDebug( 399 ) << "Image Bytes Per Line: " << img.bytesPerLine(); kDebug( 399 ) << "Image Depth: " << img.depth(); if ( !img.isNull() ) { *outImage = img; return true; } else { return false; } } bool PCXHandler::write(const QImage &image) { QDataStream s( device() ); s.setByteOrder( QDataStream::LittleEndian ); QImage img = image; int w = img.width(); int h = img.height(); kDebug( 399 ) << "Width: " << w; kDebug( 399 ) << "Height: " << h; kDebug( 399 ) << "Depth: " << img.depth(); kDebug( 399 ) << "BytesPerLine: " << img.bytesPerLine(); kDebug( 399 ) << "Num Colors: " << img.colorCount(); PCXHEADER header; header.Manufacturer = 10; header.Version = 5; header.Encoding = 1; header.XMin = 0; header.YMin = 0; header.XMax = w-1; header.YMax = h-1; header.HDpi = 300; header.YDpi = 300; header.Reserved = 0; header.PaletteInfo =1; if ( img.depth() == 1 ) { writeImage1( img, s, header ); } else if ( img.depth() == 8 && img.colorCount() <= 16 ) { writeImage4( img, s, header ); } else if ( img.depth() == 8 ) { writeImage8( img, s, header ); } else if ( img.depth() == 32 ) { writeImage24( img, s, header ); } return true; } QByteArray PCXHandler::name() const { return "pcx"; } bool PCXHandler::canRead(QIODevice *device) { if (!device) { qWarning("PCXHandler::canRead() called with no device"); return false; } qint64 oldPos = device->pos(); char head[1]; qint64 readBytes = device->read(head, sizeof(head)); if (readBytes != sizeof(head)) { if (device->isSequential()) { while (readBytes > 0) device->ungetChar(head[readBytes-- - 1]); } else { device->seek(oldPos); } return false; } if (device->isSequential()) { while (readBytes > 0) device->ungetChar(head[readBytes-- - 1]); } else { device->seek(oldPos); } return qstrncmp(head, "\012", 1) == 0; } class PCXPlugin : public QImageIOPlugin { public: QStringList keys() const; Capabilities capabilities(QIODevice *device, const QByteArray &format) const; QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; }; QStringList PCXPlugin::keys() const { return QStringList() << "pcx" << "PCX"; } QImageIOPlugin::Capabilities PCXPlugin::capabilities(QIODevice *device, const QByteArray &format) const { if (format == "pcx" || format == "PCX") return Capabilities(CanRead | CanWrite); if (!format.isEmpty()) return 0; if (!device->isOpen()) return 0; Capabilities cap; if (device->isReadable() && PCXHandler::canRead(device)) cap |= CanRead; if (device->isWritable()) cap |= CanWrite; return cap; } QImageIOHandler *PCXPlugin::create(QIODevice *device, const QByteArray &format) const { QImageIOHandler *handler = new PCXHandler; handler->setDevice(device); handler->setFormat(format); return handler; } Q_EXPORT_STATIC_PLUGIN(PCXPlugin) Q_EXPORT_PLUGIN2(pcx, PCXPlugin) /* vim: et sw=2 ts=2 */