/************************************************************** * * 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 #include #include //============================ TGAReader ================================== struct TGAFileHeader { sal_uInt8 nImageIDLength; sal_uInt8 nColorMapType; sal_uInt8 nImageType; sal_uInt16 nColorMapFirstEntryIndex; sal_uInt16 nColorMapLength; sal_uInt8 nColorMapEntrySize; sal_uInt16 nColorMapXOrigin; sal_uInt16 nColorMapYOrigin; sal_uInt16 nImageWidth; sal_uInt16 nImageHeight; sal_uInt8 nPixelDepth; sal_uInt8 nImageDescriptor; }; #define SizeOfTGAFileFooter 26 struct TGAFileFooter { sal_uInt32 nExtensionFileOffset; sal_uInt32 nDeveloperDirectoryOffset; sal_uInt32 nSignature[4]; sal_uInt8 nPadByte; sal_uInt8 nStringTerminator; }; #define SizeOfTGAExtension 495 struct TGAExtension { sal_uInt16 nExtensionSize; char sAuthorName[41]; char sAuthorComment[324]; char sDateTimeStamp[12]; char sJobNameID[41]; sal_uInt16 nJobTime[3]; char sSoftwareID[41]; sal_uInt16 nSoftwareVersionNumber; sal_uInt8 nSoftwareVersionLetter; sal_uInt32 nKeyColor; sal_uInt16 nPixelAspectRatioNumerator; sal_uInt16 nPixelAspectRatioDeNumerator; sal_uInt16 nGammaValueNumerator; sal_uInt16 nGammaValueDeNumerator; sal_uInt32 nColorCorrectionOffset; sal_uInt32 nPostageStampOffset; sal_uInt32 nScanLineOffset; sal_uInt8 nAttributesType; }; class TGAReader { private: SvStream* mpTGA; BitmapWriteAccess* mpAcc; TGAFileHeader* mpFileHeader; TGAFileFooter* mpFileFooter; TGAExtension* mpExtension; sal_uInt32* mpColorMap; sal_Bool mbStatus; sal_uLong mnTGAVersion; // Enhanced TGA is defined as Version 2.0 sal_uInt16 mnDestBitDepth; sal_Bool mbIndexing; // sal_True if source contains indexing color values sal_Bool mbEncoding; // sal_True if source is compressed sal_Bool ImplReadHeader(); sal_Bool ImplReadPalette(); sal_Bool ImplReadBody(); public: TGAReader(); ~TGAReader(); sal_Bool ReadTGA( SvStream & rTGA, Graphic & rGraphic ); }; //=================== Methoden von TGAReader ============================== TGAReader::TGAReader() : mpAcc ( NULL ), mpFileHeader ( NULL ), mpFileFooter ( NULL ), mpExtension ( NULL ), mpColorMap ( NULL ), mbStatus ( sal_True ), mnTGAVersion ( 1 ), mbIndexing ( sal_False ), mbEncoding ( sal_False ) { } TGAReader::~TGAReader() { delete[] mpColorMap; delete mpFileHeader; delete mpExtension; delete mpFileFooter; } // ------------------------------------------------------------------------------------------- sal_Bool TGAReader::ReadTGA( SvStream & rTGA, Graphic & rGraphic ) { if ( rTGA.GetError() ) return sal_False; mpTGA = &rTGA; mpTGA->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); // Kopf einlesen: if ( !mpTGA->GetError() ) { mbStatus = ImplReadHeader(); if ( mbStatus ) { Bitmap aBitmap; aBitmap = Bitmap( Size( mpFileHeader->nImageWidth, mpFileHeader->nImageHeight ), mnDestBitDepth ); mpAcc = aBitmap.AcquireWriteAccess(); if ( mpAcc ) { if ( mbIndexing ) mbStatus = ImplReadPalette(); if ( mbStatus ) mbStatus = ImplReadBody(); } else mbStatus = sal_False; if ( mpAcc ) aBitmap.ReleaseAccess ( mpAcc), mpAcc = NULL; if ( mbStatus ) rGraphic = aBitmap; } } return mbStatus; } // ------------------------------------------------------------------------------------------- sal_Bool TGAReader::ImplReadHeader() { mpFileHeader = new TGAFileHeader; if ( mpFileHeader == NULL ) return sal_False; *mpTGA >> mpFileHeader->nImageIDLength >> mpFileHeader->nColorMapType >> mpFileHeader->nImageType >> mpFileHeader->nColorMapFirstEntryIndex >> mpFileHeader->nColorMapLength >> mpFileHeader->nColorMapEntrySize >> mpFileHeader->nColorMapXOrigin >> mpFileHeader->nColorMapYOrigin >> mpFileHeader->nImageWidth >> mpFileHeader->nImageHeight >> mpFileHeader->nPixelDepth >> mpFileHeader->nImageDescriptor; if ( mpFileHeader->nColorMapType > 1 ) return sal_False; if ( mpFileHeader->nColorMapType == 1 ) mbIndexing = sal_True; // first we want to get the version mpFileFooter = new TGAFileFooter; // read the TGA-File-Footer to determine whether if ( mpFileFooter ) // we got an old TGA format or the new one { sal_uLong nCurStreamPos = mpTGA->Tell(); mpTGA->Seek( STREAM_SEEK_TO_END ); sal_uLong nTemp = mpTGA->Tell(); mpTGA->Seek( nTemp - SizeOfTGAFileFooter ); *mpTGA >> mpFileFooter->nExtensionFileOffset >> mpFileFooter->nDeveloperDirectoryOffset >> mpFileFooter->nSignature[0] >> mpFileFooter->nSignature[1] >> mpFileFooter->nSignature[2] >> mpFileFooter->nSignature[3] >> mpFileFooter->nPadByte >> mpFileFooter->nStringTerminator; // check for sal_True, VISI, ON-X, FILE in the signatures if ( mpFileFooter->nSignature[ 0 ] == (('T'<<24)|('R'<<16)|('U'<<8)|'E') && mpFileFooter->nSignature[ 1 ] == (('V'<<24)|('I'<<16)|('S'<<8)|'I') && mpFileFooter->nSignature[ 2 ] == (('O'<<24)|('N'<<16)|('-'<<8)|'X') && mpFileFooter->nSignature[ 3 ] == (('F'<<24)|('I'<<16)|('L'<<8)|'E') ) { mpExtension = new TGAExtension; if ( mpExtension ) { mpTGA->Seek( mpFileFooter->nExtensionFileOffset ); *mpTGA >> mpExtension->nExtensionSize; if ( mpExtension->nExtensionSize >= SizeOfTGAExtension ) { mnTGAVersion = 2; mpTGA->Read( mpExtension->sAuthorName, 41 ); mpTGA->Read( mpExtension->sAuthorComment, 324 ); mpTGA->Read( mpExtension->sDateTimeStamp, 12 ); mpTGA->Read( mpExtension->sJobNameID, 12 ); *mpTGA >> mpExtension->sJobNameID[ 0 ] >> mpExtension->sJobNameID[ 1 ] >> mpExtension->sJobNameID[ 2 ]; mpTGA->Read( mpExtension->sSoftwareID, 41 ); *mpTGA >> mpExtension->nSoftwareVersionNumber >> mpExtension->nSoftwareVersionLetter >> mpExtension->nKeyColor >> mpExtension->nPixelAspectRatioNumerator >> mpExtension->nPixelAspectRatioDeNumerator >> mpExtension->nGammaValueNumerator >> mpExtension->nGammaValueDeNumerator >> mpExtension->nColorCorrectionOffset >> mpExtension->nPostageStampOffset >> mpExtension->nScanLineOffset >> mpExtension->nAttributesType; } } } mpTGA->Seek( nCurStreamPos ); } // using the TGA file specification this was the correct form but adobe photoshop sets nImageDescriptor // equal to nPixelDepth // mnDestBitDepth = mpFileHeader->nPixelDepth - ( mpFileHeader->nImageDescriptor & 0xf ); mnDestBitDepth = mpFileHeader->nPixelDepth; if ( mnDestBitDepth == 8 ) // this is a patch for grayscale pictures not including a palette mbIndexing = sal_True; if ( mnDestBitDepth > 32 ) // maybe the pixeldepth is invalid return sal_False; else if ( mnDestBitDepth > 8 ) mnDestBitDepth = 24; else if ( mnDestBitDepth > 4 ) mnDestBitDepth = 8; else if ( mnDestBitDepth > 2 ) mnDestBitDepth = 4; if ( !mbIndexing && ( mnDestBitDepth < 15 ) ) return sal_False; switch ( mpFileHeader->nImageType ) { case 9 : // encoding for colortype 9, 10, 11 case 10 : case 11 : mbEncoding = sal_True; break; }; if ( mpFileHeader->nImageIDLength ) // skip the Image ID mpTGA->SeekRel( mpFileHeader->nImageIDLength ); return mbStatus; } // ------------------------------------------------------------------------------------------- sal_Bool TGAReader::ImplReadBody() { sal_uInt16 nXCount, nYCount, nRGB16; sal_uInt8 nRed, nGreen, nBlue, nRunCount, nDummy, nDepth; // this four variables match the image direction long nY, nYAdd, nX, nXAdd, nXStart; nX = nXStart = nY = 0; nXCount = nYCount = 0; nYAdd = nXAdd = 1; if ( mpFileHeader->nImageDescriptor & 0x10 ) { nX = nXStart = mpFileHeader->nImageWidth - 1; nXAdd -= 2; } if ( !(mpFileHeader->nImageDescriptor & 0x20 ) ) { nY = mpFileHeader->nImageHeight - 1; nYAdd -=2; } // nDepth = mpFileHeader->nPixelDepth - ( mpFileHeader->nImageDescriptor & 0xf ); nDepth = mpFileHeader->nPixelDepth; if ( mbEncoding ) { if ( mbIndexing ) { switch( nDepth ) { // 16 bit encoding + indexing case 16 : while ( nYCount < mpFileHeader->nImageHeight ) { *mpTGA >> nRunCount; if ( nRunCount & 0x80 ) // a run length packet { *mpTGA >> nRGB16; if ( nRGB16 >= mpFileHeader->nColorMapLength ) return sal_False; nRed = (sal_uInt8)( mpColorMap[ nRGB16 ] >> 16 ); nGreen = (sal_uInt8)( mpColorMap[ nRGB16 ] >> 8 ); nBlue = (sal_uInt8)( mpColorMap[ nRGB16 ] ); for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } else // a raw packet { for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { *mpTGA >> nRGB16; if ( nRGB16 >= mpFileHeader->nColorMapLength ) return sal_False; nRed = (sal_uInt8)( mpColorMap[ nRGB16 ] >> 16 ); nGreen = (sal_uInt8)( mpColorMap[ nRGB16 ] >> 8 ); nBlue = (sal_uInt8)( mpColorMap[ nRGB16 ] ); mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } } break; // 8 bit encoding + indexing case 8 : while ( nYCount < mpFileHeader->nImageHeight ) { *mpTGA >> nRunCount; if ( nRunCount & 0x80 ) // a run length packet { *mpTGA >> nDummy; if ( nDummy >= mpFileHeader->nColorMapLength ) return sal_False; for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { mpAcc->SetPixelIndex( nY, nX, nDummy ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } else // a raw packet { for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { *mpTGA >> nDummy; if ( nDummy >= mpFileHeader->nColorMapLength ) return sal_False; mpAcc->SetPixelIndex( nY, nX, nDummy ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } } break; default: return sal_False; } } else { switch( nDepth ) { // 32 bit transparent true color encoding case 32 : { while ( nYCount < mpFileHeader->nImageHeight ) { *mpTGA >> nRunCount; if ( nRunCount & 0x80 ) // a run length packet { *mpTGA >> nBlue >> nGreen >> nRed >> nDummy; for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } else // a raw packet { for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { *mpTGA >> nBlue >> nGreen >> nRed >> nDummy; mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } } } break; // 24 bit true color encoding case 24 : while ( nYCount < mpFileHeader->nImageHeight ) { *mpTGA >> nRunCount; if ( nRunCount & 0x80 ) // a run length packet { *mpTGA >> nBlue >> nGreen >> nRed; for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } else // a raw packet { for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { *mpTGA >> nBlue >> nGreen >> nRed; mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } } break; // 16 bit true color encoding case 16 : while ( nYCount < mpFileHeader->nImageHeight ) { *mpTGA >> nRunCount; if ( nRunCount & 0x80 ) // a run length packet { *mpTGA >> nRGB16; nRed = (sal_uInt8)( nRGB16 >> 7 ) & 0xf8; nGreen = (sal_uInt8)( nRGB16 >> 2 ) & 0xf8; nBlue = (sal_uInt8)( nRGB16 << 3 ) & 0xf8; for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } else // a raw packet { for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ ) { *mpTGA >> nRGB16; nRed = (sal_uInt8)( nRGB16 >> 7 ) & 0xf8; nGreen = (sal_uInt8)( nRGB16 >> 2 ) & 0xf8; nBlue = (sal_uInt8)( nRGB16 << 3 ) & 0xf8; mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); nX += nXAdd; nXCount++; if ( nXCount == mpFileHeader->nImageWidth ) { nX = nXStart; nXCount = 0; nY += nYAdd; nYCount++; if( nYCount >= mpFileHeader->nImageHeight ) return false; // invalid picture } } } } break; default: return sal_False; } } } else { for ( nYCount = 0; nYCount < mpFileHeader->nImageHeight; nYCount++, nY += nYAdd ) { nX = nXStart; nXCount = 0; if ( mbIndexing ) { switch( nDepth ) { // 16 bit indexing case 16 : for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd ) { *mpTGA >> nRGB16; if ( nRGB16 >= mpFileHeader->nColorMapLength ) return sal_False; nRed = (sal_uInt8)( mpColorMap[ nRGB16 ] >> 16 ); nGreen = (sal_uInt8)( mpColorMap[ nRGB16 ] >> 8 ); nBlue = (sal_uInt8)( mpColorMap[ nRGB16 ] ); mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); } break; // 8 bit indexing case 8 : for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd ) { *mpTGA >> nDummy; if ( nDummy >= mpFileHeader->nColorMapLength ) return sal_False; mpAcc->SetPixelIndex( nY, nX, nDummy ); } break; default: return sal_False; } } else { switch( nDepth ) { // 32 bit true color case 32 : { for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd ) { *mpTGA >> nBlue >> nGreen >> nRed >> nDummy; mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); } } break; // 24 bit true color case 24 : for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd ) { *mpTGA >> nBlue >> nGreen >> nRed; mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); } break; // 16 bit true color case 16 : for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd ) { *mpTGA >> nRGB16; nRed = (sal_uInt8)( nRGB16 >> 7 ) & 0xf8; nGreen = (sal_uInt8)( nRGB16 >> 2 ) & 0xf8; nBlue = (sal_uInt8)( nRGB16 << 3 ) & 0xf8; mpAcc->SetPixel( nY, nX, BitmapColor( nRed, nGreen, nBlue ) ); } break; default: return sal_False; } } } } return mbStatus; } // ------------------------------------------------------------------------------------------- sal_Bool TGAReader::ImplReadPalette() { if ( mbIndexing ) // read the colormap { sal_uInt16 nColors = mpFileHeader->nColorMapLength; if ( !nColors ) // colors == 0 ? -> we will build a grayscale palette { if ( mpFileHeader->nPixelDepth != 8 ) return sal_False; nColors = 256; mpFileHeader->nColorMapLength = 256; mpFileHeader->nColorMapEntrySize = 0x3f; // patch for the following switch routine } mpColorMap = new sal_uInt32[ nColors ]; // we will always index dwords if ( !mpColorMap ) // NOTE: check for new-result==NULL because exceptions are disabled in this module return sal_False; // out of memory %&!$&/!"�$ switch( mpFileHeader->nColorMapEntrySize ) { case 0x3f : { for ( sal_uLong i = 0; i < nColors; i++ ) { mpColorMap[ i ] = ( i << 16 ) + ( i << 8 ) + i; } } break; case 32 : mpTGA->Read( mpColorMap, 4 * nColors ); break; case 24 : { for ( sal_uLong i = 0; i < nColors; i++ ) { mpTGA->Read( &mpColorMap[ i ], 3 ); } } break; case 15 : case 16 : { for ( sal_uLong i = 0; i < nColors; i++ ) { sal_uInt16 nTemp; *mpTGA >> nTemp; mpColorMap[ i ] = ( ( nTemp & 0x7c00 ) << 9 ) + ( ( nTemp & 0x01e0 ) << 6 ) + ( ( nTemp & 0x1f ) << 3 ); } } break; default : return sal_False; } if ( mnDestBitDepth <= 8 ) { sal_uInt16 nDestColors = ( 1 << mnDestBitDepth ); if ( nColors > nDestColors ) return sal_False; mpAcc->SetPaletteEntryCount( nColors ); for ( sal_uInt16 i = 0; i < nColors; i++ ) { mpAcc->SetPaletteColor( i, Color( (sal_uInt8)( mpColorMap[ i ] >> 16 ), (sal_uInt8)( mpColorMap[ i ] >> 8 ), (sal_uInt8)(mpColorMap[ i ] ) ) ); } } } return mbStatus; } //================== GraphicImport - die exportierte Funktion ================ extern "C" sal_Bool __LOADONCALLAPI GraphicImport(SvStream & rStream, Graphic & rGraphic, FilterConfigItem*, sal_Bool ) { TGAReader aTGAReader; return aTGAReader.ReadTGA( rStream, rGraphic ); }