diff options
Diffstat (limited to 'kimgio/psd.cpp')
-rw-r--r-- | kimgio/psd.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/kimgio/psd.cpp b/kimgio/psd.cpp new file mode 100644 index 000000000..202fbacb4 --- /dev/null +++ b/kimgio/psd.cpp @@ -0,0 +1,282 @@ +/* This file is part of the KDE project + Copyright (C) 2003 Ignacio Castaņo <castano@ludicon.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This code is based on Thacher Ulrich PSD loading code released + on public domain. See: http://tulrich.com/geekstuff/ +*/ + +/* this code supports: + * reading: + * rle and raw psd files + * writing: + * not supported + */ + +#include "psd.h" + +#include <qimage.h> +#include <qdatastream.h> + +#include <kdebug.h> + +typedef Q_UINT32 uint; +typedef Q_UINT16 ushort; +typedef Q_UINT8 uchar; + +namespace { // Private. + + enum ColorMode { + CM_BITMAP = 0, + CM_GRAYSCALE = 1, + CM_INDEXED = 2, + CM_RGB = 3, + CM_CMYK = 4, + CM_MULTICHANNEL = 7, + CM_DUOTONE = 8, + CM_LABCOLOR = 9 + }; + + struct PSDHeader { + uint signature; + ushort version; + uchar reserved[6]; + ushort channel_count; + uint height; + uint width; + ushort depth; + ushort color_mode; + }; + + static QDataStream & operator>> ( QDataStream & s, PSDHeader & header ) + { + s >> header.signature; + s >> header.version; + for( int i = 0; i < 6; i++ ) { + s >> header.reserved[i]; + } + s >> header.channel_count; + s >> header.height; + s >> header.width; + s >> header.depth; + s >> header.color_mode; + return s; + } + static bool seekBy(QDataStream& s, unsigned int bytes) + { + char buf[4096]; + while (bytes) { + unsigned int num= QMIN(bytes,sizeof(buf)); + unsigned int l = num; + s.readRawBytes(buf, l); + if(l != num) + return false; + bytes -= num; + } + return true; + } + + // Check that the header is a valid PSD. + static bool IsValid( const PSDHeader & header ) + { + if( header.signature != 0x38425053 ) { // '8BPS' + return false; + } + return true; + } + + // Check that the header is supported. + static bool IsSupported( const PSDHeader & header ) + { + if( header.version != 1 ) { + return false; + } + if( header.channel_count > 16 ) { + return false; + } + if( header.depth != 8 ) { + return false; + } + if( header.color_mode != CM_RGB ) { + return false; + } + return true; + } + + // Load the PSD image. + static bool LoadPSD( QDataStream & s, const PSDHeader & header, QImage & img ) + { + // Create dst image. + if( !img.create( header.width, header.height, 32 )) { + return false; + } + + uint tmp; + + // Skip mode data. + s >> tmp; + s.device()->at( s.device()->at() + tmp ); + + // Skip image resources. + s >> tmp; + s.device()->at( s.device()->at() + tmp ); + + // Skip the reserved data. + s >> tmp; + s.device()->at( s.device()->at() + tmp ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + ushort compression; + s >> compression; + + if( compression > 1 ) { + // Unknown compression type. + return false; + } + + uint channel_num = header.channel_count; + + // Clear the image. + if( channel_num < 4 ) { + img.fill(qRgba(0, 0, 0, 0xFF)); + } + else { + // Enable alpha. + img.setAlphaBuffer( true ); + + // Ignore the other channels. + channel_num = 4; + } + + const uint pixel_count = header.height * header.width; + + static const uint components[4] = {2, 1, 0, 3}; // @@ Is this endian dependant? + + if( compression ) { + + // Skip row lengths. + if(!seekBy(s, header.height*header.channel_count*sizeof(ushort))) + return false; + + // Read RLE data. + for(uint channel = 0; channel < channel_num; channel++) { + + uchar * ptr = img.bits() + components[channel]; + + uint count = 0; + while( count < pixel_count ) { + uchar c; + if(s.atEnd()) + return false; + s >> c; + uint len = c; + + if( len < 128 ) { + // Copy next len+1 bytes literally. + len++; + count += len; + if ( count > pixel_count ) + return false; + + while( len != 0 ) { + s >> *ptr; + ptr += 4; + len--; + } + } + else if( len > 128 ) { + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0xFF; + len += 2; + count += len; + if(s.atEnd() || count > pixel_count) + return false; + uchar val; + s >> val; + while( len != 0 ) { + *ptr = val; + ptr += 4; + len--; + } + } + else if( len == 128 ) { + // No-op. + } + } + } + } + else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for(uint channel = 0; channel < channel_num; channel++) { + + uchar * ptr = img.bits() + components[channel]; + + // Read the data. + uint count = pixel_count; + while( count != 0 ) { + s >> *ptr; + ptr += 4; + count--; + } + } + } + + return true; + } + +} // Private + + +void kimgio_psd_read( QImageIO *io ) +{ + QDataStream s( io->ioDevice() ); + s.setByteOrder( QDataStream::BigEndian ); + + PSDHeader header; + s >> header; + + // Check image file format. + if( s.atEnd() || !IsValid( header ) ) { + kdDebug(399) << "This PSD file is not valid." << endl; + io->setImage( 0 ); + io->setStatus( -1 ); + return; + } + + // Check if it's a supported format. + if( !IsSupported( header ) ) { + kdDebug(399) << "This PSD file is not supported." << endl; + io->setImage( 0 ); + io->setStatus( -1 ); + return; + } + + QImage img; + if( !LoadPSD(s, header, img) ) { + kdDebug(399) << "Error loading PSD file." << endl; + io->setImage( 0 ); + io->setStatus( -1 ); + return; + } + + io->setImage( img ); + io->setStatus( 0 ); +} + + +void kimgio_psd_write( QImageIO * ) +{ + // TODO Stub! +} + |