blob: 9751c37e30093be5c239000aa3ab08023049b9fd [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_filter.hxx"
#include <vcl/graph.hxx>
#include <vcl/bmpacc.hxx>
#include <svtools/fltcall.hxx>
//============================ PSDReader ==================================
#define PSD_BITMAP 0
#define PSD_GRAYSCALE 1
#define PSD_INDEXED 2
#define PSD_RGB 3
#define PSD_CMYK 4
#define PSD_MULTICHANNEL 7
#define PSD_DUOTONE 8
#define PSD_LAB 9
typedef struct
{
sal_uInt32 nSignature;
sal_uInt16 nVersion;
sal_uInt32 nPad1;
sal_uInt16 nPad2;
sal_uInt16 nChannels;
sal_uInt32 nRows;
sal_uInt32 nColumns;
sal_uInt16 nDepth;
sal_uInt16 nMode;
} PSDFileHeader;
class PSDReader {
private:
SvStream* mpPSD; // Die einzulesende PSD-Datei
PSDFileHeader* mpFileHeader;
sal_uInt32 mnXResFixed;
sal_uInt32 mnYResFixed;
sal_Bool mbStatus;
sal_Bool mbTransparent;
Bitmap maBmp;
Bitmap maMaskBmp;
BitmapReadAccess* mpReadAcc;
BitmapWriteAccess* mpWriteAcc;
BitmapWriteAccess* mpMaskWriteAcc;
sal_uInt16 mnDestBitDepth;
sal_Bool mbCompression; // RLE decoding
sal_uInt8* mpPalette;
sal_Bool ImplReadBody();
sal_Bool ImplReadHeader();
public:
PSDReader();
~PSDReader();
sal_Bool ReadPSD( SvStream & rPSD, Graphic & rGraphic );
};
//=================== Methoden von PSDReader ==============================
PSDReader::PSDReader() :
mpFileHeader ( NULL ),
mnXResFixed ( 0 ),
mnYResFixed ( 0 ),
mbStatus ( sal_True ),
mbTransparent ( sal_False ),
mpReadAcc ( NULL ),
mpWriteAcc ( NULL ),
mpMaskWriteAcc ( NULL ),
mpPalette ( NULL )
{
}
PSDReader::~PSDReader()
{
delete[] mpPalette;
delete mpFileHeader;
}
// ------------------------------------------------------------------------
sal_Bool PSDReader::ReadPSD( SvStream & rPSD, Graphic & rGraphic )
{
if ( rPSD.GetError() )
return sal_False;
mpPSD = &rPSD;
mpPSD->SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
// Kopf einlesen:
if ( ImplReadHeader() == sal_False )
return sal_False;
Size aBitmapSize( mpFileHeader->nColumns, mpFileHeader->nRows );
maBmp = Bitmap( aBitmapSize, mnDestBitDepth );
if ( ( mpWriteAcc = maBmp.AcquireWriteAccess() ) == NULL )
mbStatus = sal_False;
if ( ( mpReadAcc = maBmp.AcquireReadAccess() ) == NULL )
mbStatus = sal_False;
if ( mbTransparent && mbStatus )
{
maMaskBmp = Bitmap( aBitmapSize, 1 );
if ( ( mpMaskWriteAcc = maMaskBmp.AcquireWriteAccess() ) == NULL )
mbStatus = sal_False;
}
if ( mpPalette && mbStatus )
{
mpWriteAcc->SetPaletteEntryCount( 256 );
for ( sal_uInt16 i = 0; i < 256; i++ )
{
mpWriteAcc->SetPaletteColor( i, Color( mpPalette[ i ], mpPalette[ i + 256 ], mpPalette[ i + 512 ] ) );
}
}
// Bitmap-Daten einlesen
if ( mbStatus && ImplReadBody() )
{
if ( mbTransparent )
rGraphic = Graphic( BitmapEx( maBmp, maMaskBmp ) );
else
rGraphic = maBmp;
if ( mnXResFixed && mnYResFixed )
{
Point aEmptyPoint;
Fraction aFractX( 1, mnXResFixed >> 16 );
Fraction aFractY( 1, mnYResFixed >> 16 );
MapMode aMapMode( MAP_INCH, aEmptyPoint, aFractX, aFractY );
Size aPrefSize = OutputDevice::LogicToLogic( aBitmapSize, aMapMode, MAP_100TH_MM );
rGraphic.SetPrefSize( aPrefSize );
rGraphic.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
}
}
else
mbStatus = sal_False;
if ( mpWriteAcc )
maBmp.ReleaseAccess( mpWriteAcc );
if ( mpReadAcc )
maBmp.ReleaseAccess( mpReadAcc );
if ( mpMaskWriteAcc )
maMaskBmp.ReleaseAccess( mpMaskWriteAcc );
return mbStatus;
}
// ------------------------------------------------------------------------
sal_Bool PSDReader::ImplReadHeader()
{
sal_uInt16 nCompression;
sal_uInt32 nColorLength, nResourceLength, nLayerMaskLength;
mpFileHeader = new PSDFileHeader;
if ( !mpFileHeader )
return sal_False;
*mpPSD >> mpFileHeader->nSignature >> mpFileHeader->nVersion >> mpFileHeader->nPad1 >>
mpFileHeader->nPad2 >> mpFileHeader->nChannels >> mpFileHeader->nRows >>
mpFileHeader->nColumns >> mpFileHeader->nDepth >> mpFileHeader->nMode;
if ( ( mpFileHeader->nSignature != 0x38425053 ) || ( mpFileHeader->nVersion != 1 ) )
return sal_False;
if ( mpFileHeader->nRows == 0 || mpFileHeader->nColumns == 0 )
return sal_False;
if ( ( mpFileHeader->nRows > 30000 ) || ( mpFileHeader->nColumns > 30000 ) )
return sal_False;
sal_uInt16 nDepth = mpFileHeader->nDepth;
if (!( ( nDepth == 1 ) || ( nDepth == 8 ) || ( nDepth == 16 ) ) )
return sal_False;
mnDestBitDepth = ( nDepth == 16 ) ? 8 : nDepth;
*mpPSD >> nColorLength;
if ( mpFileHeader->nMode == PSD_CMYK )
{
switch ( mpFileHeader->nChannels )
{
case 5 :
mbTransparent = sal_True;
case 4 :
mnDestBitDepth = 24;
break;
default :
return sal_False;
}
}
else switch ( mpFileHeader->nChannels )
{
case 2 :
mbTransparent = sal_True;
case 1 :
break;
case 4 :
mbTransparent = sal_True;
case 3 :
mnDestBitDepth = 24;
break;
default:
return sal_False;
}
switch ( mpFileHeader->nMode )
{
case PSD_BITMAP :
{
if ( nColorLength || ( nDepth != 1 ) )
return sal_False;
}
break;
case PSD_INDEXED :
{
if ( nColorLength != 768 ) // we need the color map
return sal_False;
mpPalette = new sal_uInt8[ 768 ];
if ( mpPalette == NULL )
return sal_False;
mpPSD->Read( mpPalette, 768 );
}
break;
case PSD_DUOTONE : // we'll handle the doutone color like a normal grayscale picture
mpPSD->SeekRel( nColorLength );
nColorLength = 0;
case PSD_GRAYSCALE :
{
if ( nColorLength )
return sal_False;
mpPalette = new sal_uInt8[ 768 ];
if ( mpPalette == NULL )
return sal_False;
for ( sal_uInt16 i = 0; i < 256; i++ )
{
mpPalette[ i ] = mpPalette[ i + 256 ] = mpPalette[ i + 512 ] = (sal_uInt8)i;
}
}
break;
case PSD_CMYK :
case PSD_RGB :
case PSD_MULTICHANNEL :
case PSD_LAB :
{
if ( nColorLength ) // color table is not supported by the other graphic modes
return sal_False;
}
break;
default:
return sal_False;
}
*mpPSD >> nResourceLength;
sal_uInt32 nLayerPos = mpPSD->Tell() + nResourceLength;
// this is a loop over the resource entries to get the resolution info
while( mpPSD->Tell() < nLayerPos )
{
sal_uInt8 n8;
sal_uInt32 nType, nPStringLen, nResEntryLen;
sal_uInt16 nUniqueID;
*mpPSD >> nType >> nUniqueID >> n8;
nPStringLen = n8;
if ( nType != 0x3842494d )
break;
if ( ! ( nPStringLen & 1 ) )
nPStringLen++;
mpPSD->SeekRel( nPStringLen ); // skipping the pstring
*mpPSD >> nResEntryLen;
if ( nResEntryLen & 1 )
nResEntryLen++; // the resource entries are padded
sal_uInt32 nCurrentPos = mpPSD->Tell();
if ( ( nResEntryLen + nCurrentPos ) > nLayerPos ) // check if size
break; // is possible
switch( nUniqueID )
{
case 0x3ed : // UID for the resolution info
{
sal_Int16 nUnit;
*mpPSD >> mnXResFixed >> nUnit >> nUnit
>> mnYResFixed >> nUnit >> nUnit;
}
break;
}
mpPSD->Seek( nCurrentPos + nResEntryLen ); // set the stream to the next
} // resource entry
mpPSD->Seek( nLayerPos );
*mpPSD >> nLayerMaskLength;
mpPSD->SeekRel( nLayerMaskLength );
*mpPSD >> nCompression;
if ( nCompression == 0 )
{
mbCompression = sal_False;
}
else if ( nCompression == 1 )
{
mpPSD->SeekRel( ( mpFileHeader->nRows * mpFileHeader->nChannels ) << 1 );
mbCompression = sal_True;
}
else
return sal_False;
return sal_True;
}
// ------------------------------------------------------------------------
sal_Bool PSDReader::ImplReadBody()
{
sal_uLong nX, nY;
char nRunCount = 0;
signed char nBitCount = -1;
sal_uInt8 nDat = 0, nDummy, nRed, nGreen, nBlue;
BitmapColor aBitmapColor;
nX = nY = 0;
switch ( mnDestBitDepth )
{
case 1 :
{
while ( nY < mpFileHeader->nRows )
{
if ( nBitCount == -1 )
{
if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
*mpPSD >> nRunCount;
}
if ( nRunCount & 0x80 ) // a run length packet
{
if ( nBitCount == -1 ) // bits left in nDat ?
{
*mpPSD >> nDat;
nDat ^= 0xff;
nBitCount = 7;
}
for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
{
mpWriteAcc->SetPixelIndex( nY, nX, nDat >> nBitCount-- );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
nBitCount = -1;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
else // a raw packet
{
for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
{
if ( nBitCount == -1 ) // bits left in nDat ?
{
*mpPSD >> nDat;
nDat ^= 0xff;
nBitCount = 7;
}
mpWriteAcc->SetPixelIndex( nY, nX, nDat >> nBitCount-- );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
nBitCount = -1;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
}
}
break;
case 8 :
{
while ( nY < mpFileHeader->nRows )
{
if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
*mpPSD >> nRunCount;
if ( nRunCount & 0x80 ) // a run length packet
{
*mpPSD >> nDat;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
{
mpWriteAcc->SetPixelIndex( nY, nX, nDat );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
else // a raw packet
{
for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
{
*mpPSD >> nDat;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
mpWriteAcc->SetPixelIndex( nY, nX, nDat );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
}
}
break;
case 24 :
{
// the psd format is in plain order (RRRR GGGG BBBB) so we have to set each pixel three times
// maybe the format is CCCC MMMM YYYY KKKK
while ( nY < mpFileHeader->nRows )
{
if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
*mpPSD >> nRunCount;
if ( nRunCount & 0x80 ) // a run length packet
{
*mpPSD >> nRed;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
{
mpWriteAcc->SetPixel( nY, nX, BitmapColor( nRed, (sal_uInt8)0, (sal_uInt8)0 ) );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
else // a raw packet
{
for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
{
*mpPSD >> nRed;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
mpWriteAcc->SetPixel( nY, nX, BitmapColor( nRed, (sal_uInt8)0, (sal_uInt8)0 ) );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
}
nY = 0;
while ( nY < mpFileHeader->nRows )
{
if ( mbCompression )
*mpPSD >> nRunCount;
if ( nRunCount & 0x80 ) // a run length packet
{
*mpPSD >> nGreen;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
{
aBitmapColor = mpReadAcc->GetPixel( nY, nX );
mpWriteAcc->SetPixel( nY, nX, BitmapColor( aBitmapColor.GetRed(), nGreen, aBitmapColor.GetBlue() ) );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
else // a raw packet
{
for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
{
*mpPSD >> nGreen;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
aBitmapColor = mpReadAcc->GetPixel( nY, nX );
mpWriteAcc->SetPixel( nY, nX, BitmapColor( aBitmapColor.GetRed(), nGreen, aBitmapColor.GetBlue() ) );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
}
nY = 0;
while ( nY < mpFileHeader->nRows )
{
if ( mbCompression )
*mpPSD >> nRunCount;
if ( nRunCount & 0x80 ) // a run length packet
{
*mpPSD >> nBlue;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
{
aBitmapColor = mpReadAcc->GetPixel( nY, nX );
mpWriteAcc->SetPixel( nY, nX, BitmapColor( aBitmapColor.GetRed(), aBitmapColor.GetGreen(), nBlue ) );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
else // a raw packet
{
for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
{
*mpPSD >> nBlue;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
aBitmapColor = mpReadAcc->GetPixel( nY, nX );
mpWriteAcc->SetPixel( nY, nX, BitmapColor( aBitmapColor.GetRed(), aBitmapColor.GetGreen(), nBlue ) );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
}
if ( mpFileHeader->nMode == PSD_CMYK )
{
sal_uInt32 nBlack, nBlackMax = 0;
sal_uInt8* pBlack = new sal_uInt8[ mpFileHeader->nRows * mpFileHeader->nColumns ];
nY = 0;
while ( nY < mpFileHeader->nRows )
{
if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
*mpPSD >> nRunCount;
if ( nRunCount & 0x80 ) // a run length packet
{
*mpPSD >> nDat;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
{
nBlack = (sal_uInt8)mpReadAcc->GetPixel( nY, nX ).GetRed() + nDat;
if ( nBlack > nBlackMax )
nBlackMax = nBlack;
nBlack = (sal_uInt8)mpReadAcc->GetPixel( nY, nX ).GetGreen() + nDat;
if ( nBlack > nBlackMax )
nBlackMax = nBlack;
nBlack = (sal_uInt8)mpReadAcc->GetPixel( nY, nX ).GetBlue() + nDat;
if ( nBlack > nBlackMax )
nBlackMax = nBlack;
pBlack[ nX + nY * mpFileHeader->nColumns ] = nDat ^ 0xff;
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
else // a raw packet
{
for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
{
*mpPSD >> nDat;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
nBlack = (sal_uInt8)mpReadAcc->GetPixel( nY, nX ).GetRed() + nDat;
if ( nBlack > nBlackMax )
nBlackMax = nBlack;
nBlack = (sal_uInt8)mpReadAcc->GetPixel( nY, nX ).GetGreen() + nDat;
if ( nBlack > nBlackMax )
nBlackMax = nBlack;
nBlack = (sal_uInt8)mpReadAcc->GetPixel( nY, nX ).GetBlue() + nDat;
if ( nBlack > nBlackMax )
nBlackMax = nBlack;
pBlack[ nX + nY * mpFileHeader->nColumns ] = nDat ^ 0xff;
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
}
for ( nY = 0; nY < mpFileHeader->nRows; nY++ )
{
for ( nX = 0; nX < mpFileHeader->nColumns; nX++ )
{
sal_Int32 nDAT = pBlack[ nX + nY * mpFileHeader->nColumns ] * ( nBlackMax - 256 ) / 0x1ff;
aBitmapColor = mpReadAcc->GetPixel( nY, nX );
sal_uInt8 cR = (sal_uInt8) MinMax( aBitmapColor.GetRed() - nDAT, 0L, 255L );
sal_uInt8 cG = (sal_uInt8) MinMax( aBitmapColor.GetGreen() - nDAT, 0L, 255L );
sal_uInt8 cB = (sal_uInt8) MinMax( aBitmapColor.GetBlue() - nDAT, 0L, 255L );
mpWriteAcc->SetPixel( nY, nX, BitmapColor( cR, cG, cB ) );
}
}
delete[] pBlack;
}
}
break;
}
if ( mbTransparent )
{
// the psd is 24 or 8 bit grafix + alphachannel
nY = nX = 0;
while ( nY < mpFileHeader->nRows )
{
if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
*mpPSD >> nRunCount;
if ( nRunCount & 0x80 ) // a run length packet
{
*mpPSD >> nDat;
if ( nDat )
nDat = 0;
else
nDat = 1;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
{
mpMaskWriteAcc->SetPixelIndex( nY, nX, nDat );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
else // a raw packet
{
for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
{
*mpPSD >> nDat;
if ( nDat )
nDat = 0;
else
nDat = 1;
if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
*mpPSD >> nDummy;
mpMaskWriteAcc->SetPixelIndex( nY, nX, nDat );
if ( ++nX == mpFileHeader->nColumns )
{
nX = 0;
nY++;
if ( nY == mpFileHeader->nRows )
break;
}
}
}
}
}
return sal_True;
}
//================== GraphicImport - die exportierte Funktion ================
extern "C" sal_Bool __LOADONCALLAPI GraphicImport(SvStream & rStream, Graphic & rGraphic, FilterConfigItem*, sal_Bool )
{
PSDReader aPSDReader;
return aPSDReader.ReadPSD( rStream, rGraphic );
}