blob: 32ee72967a6ed335f6db14664a86e5248e36da57 [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/svapp.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/bmpacc.hxx>
#include <svl/solar.hrc>
#include <svtools/fltcall.hxx>
#include <svtools/FilterConfigItem.hxx>
#define NewSubfileType 254
#define ImageWidth 256
#define ImageLength 257
#define BitsPerSample 258
#define Compression 259
#define PhotometricInterpretation 262
#define StripOffsets 273
#define SamplesPerPixel 277
#define RowsPerStrip 278
#define StripByteCounts 279
#define XResolution 282
#define YResolution 283
#define PlanarConfiguration 284
#define ResolutionUnit 296
#define ColorMap 320
#define ReferenceBlackWhite 532
// -------------
// - TIFFWriter -
// -------------
struct TIFFLZWCTreeNode
{
TIFFLZWCTreeNode* pBrother; // naechster Knoten, der den selben Vater hat
TIFFLZWCTreeNode* pFirstChild; // erster Sohn
sal_uInt16 nCode; // Der Code fuer den String von Pixelwerten, der sich ergibt, wenn
sal_uInt16 nValue; // Der Pixelwert
};
class TIFFWriter
{
private:
SvStream* mpOStm;
sal_uInt32 mnStreamOfs;
sal_Bool mbStatus;
BitmapReadAccess* mpAcc;
sal_uInt32 mnWidth, mnHeight, mnColors;
sal_uInt32 mnCurAllPictHeight;
sal_uInt32 mnSumOfAllPictHeight;
sal_uInt32 mnBitsPerPixel;
sal_uInt32 mnLastPercent;
sal_uInt32 mnLatestIfdPos;
sal_uInt16 mnTagCount; // number of tags already written
sal_uInt32 mnCurrentTagCountPos; // offset to the position where the current
// tag count is to insert
sal_uInt32 mnXResPos; // if != 0 this DWORDs stores the
sal_uInt32 mnYResPos; // actual streamposition of the
sal_uInt32 mnPalPos; // Tag Entry
sal_uInt32 mnBitmapPos;
sal_uInt32 mnStripByteCountPos;
TIFFLZWCTreeNode* pTable;
TIFFLZWCTreeNode* pPrefix;
sal_uInt16 nDataSize;
sal_uInt16 nClearCode;
sal_uInt16 nEOICode;
sal_uInt16 nTableSize;
sal_uInt16 nCodeSize;
sal_uLong nOffset;
sal_uLong dwShift;
com::sun::star::uno::Reference< com::sun::star::task::XStatusIndicator > xStatusIndicator;
void ImplCallback( sal_uInt32 nPercent );
sal_Bool ImplWriteHeader( sal_Bool bMultiPage );
void ImplWritePalette();
sal_Bool ImplWriteBody();
void ImplWriteTag( sal_uInt16 TagID, sal_uInt16 DataType, sal_uInt32 NumberOfItems, sal_uInt32 Value);
void ImplWriteResolution( sal_uLong nStreamPos, sal_uInt32 nResolutionUnit );
void StartCompression();
void Compress( sal_uInt8 nSrc );
void EndCompression();
inline void WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen );
public:
TIFFWriter();
~TIFFWriter();
sal_Bool WriteTIFF( const Graphic& rGraphic, SvStream& rTIFF, FilterConfigItem* pFilterConfigItem );
};
// ------------------------------------------------------------------------
TIFFWriter::TIFFWriter() :
mbStatus ( sal_True ),
mpAcc ( NULL ),
mnCurAllPictHeight ( 0 ),
mnSumOfAllPictHeight( 0 ),
mnLastPercent ( 0 ),
mnXResPos ( 0 ),
mnYResPos ( 0 ),
mnBitmapPos ( 0 ),
mnStripByteCountPos ( 0 )
{
}
// ------------------------------------------------------------------------
TIFFWriter::~TIFFWriter()
{
}
// ------------------------------------------------------------------------
sal_Bool TIFFWriter::WriteTIFF( const Graphic& rGraphic, SvStream& rTIFF, FilterConfigItem* pFilterConfigItem)
{
sal_uLong* pDummy = new sal_uLong; delete pDummy; // damit unter OS/2
// das richtige (Tools-)new
// verwendet wird, da es sonst
// in dieser DLL nur Vector-news
// gibt;
if ( pFilterConfigItem )
{
xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
if ( xStatusIndicator.is() )
{
rtl::OUString aMsg;
xStatusIndicator->start( aMsg, 100 );
}
}
// #i69169# copy stream
mpOStm = &rTIFF;
const sal_uInt16 nOldFormat = mpOStm->GetNumberFormatInt();
mnStreamOfs = mpOStm->Tell();
// we will use the BIG Endian Mode
// TIFF header
mpOStm->SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
*mpOStm << (sal_uInt32)0x4d4d002a; // TIFF identifier
mnLatestIfdPos = mpOStm->Tell();
*mpOStm << (sal_uInt32)0;
Animation aAnimation;
Bitmap aBmp;
if( mbStatus )
{
if ( rGraphic.IsAnimated() )
aAnimation = rGraphic.GetAnimation();
else
{
AnimationBitmap aAnimationBitmap( rGraphic.GetBitmap(), Point(), Size() );
aAnimation.Insert( aAnimationBitmap );
}
sal_uInt16 i;
for ( i = 0; i < aAnimation.Count(); i++ )
mnSumOfAllPictHeight += aAnimation.Get( i ).aBmpEx.GetSizePixel().Height();
for ( i = 0; mbStatus && ( i < aAnimation.Count() ); i++ )
{
mnPalPos = 0;
const AnimationBitmap& rAnimationBitmap = aAnimation.Get( i );
aBmp = rAnimationBitmap.aBmpEx.GetBitmap();
mpAcc = aBmp.AcquireReadAccess();
if ( mpAcc )
{
mnBitsPerPixel = aBmp.GetBitCount();
// export code below only handles four discrete cases
mnBitsPerPixel =
mnBitsPerPixel <= 1 ? 1 : mnBitsPerPixel <= 4 ? 4 : mnBitsPerPixel <= 8 ? 8 : 24;
if ( ImplWriteHeader( ( aAnimation.Count() > 0 ) ) )
{
Size aDestMapSize( 300, 300 );
const MapMode aMapMode( aBmp.GetPrefMapMode() );
if ( aMapMode.GetMapUnit() != MAP_PIXEL )
{
const Size aPrefSize( rGraphic.GetPrefSize() );
aDestMapSize = OutputDevice::LogicToLogic( aPrefSize, aMapMode, MAP_INCH );
}
ImplWriteResolution( mnXResPos, aDestMapSize.Width() );
ImplWriteResolution( mnYResPos, aDestMapSize.Height() );
if ( mnPalPos )
ImplWritePalette();
ImplWriteBody();
}
sal_uInt32 nCurPos = mpOStm->Tell();
mpOStm->Seek( mnCurrentTagCountPos );
*mpOStm << mnTagCount;
mpOStm->Seek( nCurPos );
aBmp.ReleaseAccess( mpAcc );
}
else
mbStatus = sal_False;
}
}
mpOStm->SetNumberFormatInt( nOldFormat );
if ( xStatusIndicator.is() )
xStatusIndicator->end();
return mbStatus;
}
// ------------------------------------------------------------------------
void TIFFWriter::ImplCallback( sal_uInt32 nPercent )
{
if ( xStatusIndicator.is() )
{
if( nPercent >= mnLastPercent + 3 )
{
mnLastPercent = nPercent;
if ( nPercent <= 100 )
xStatusIndicator->setValue( nPercent );
}
}
}
// ------------------------------------------------------------------------
sal_Bool TIFFWriter::ImplWriteHeader( sal_Bool bMultiPage )
{
mnTagCount = 0;
mnWidth = mpAcc->Width();
mnHeight = mpAcc->Height();
if ( mnWidth && mnHeight && mnBitsPerPixel && mbStatus )
{
sal_uInt32 nCurrentPos = mpOStm->Tell();
mpOStm->Seek( mnLatestIfdPos );
*mpOStm << (sal_uInt32)( nCurrentPos - mnStreamOfs ); // offset to the IFD
mpOStm->Seek( nCurrentPos );
// (OFS8) TIFF image file directory (IFD)
mnCurrentTagCountPos = mpOStm->Tell();
*mpOStm << (sal_uInt16)0; // the number of tagentrys is to insert later
sal_uInt32 nSubFileFlags = 0;
if ( bMultiPage )
nSubFileFlags |= 2;
ImplWriteTag( NewSubfileType, 4, 1, nSubFileFlags );
ImplWriteTag( ImageWidth, 4, 1, mnWidth );
ImplWriteTag( ImageLength, 4, 1, mnHeight);
ImplWriteTag( BitsPerSample, 3, 1, ( mnBitsPerPixel == 24 ) ? 8 : mnBitsPerPixel );
ImplWriteTag( Compression, 3, 1, 5 );
sal_uInt8 nTemp;
switch ( mnBitsPerPixel )
{
case 1 :
nTemp = 1;
break;
case 4 :
case 8 :
nTemp = 3;
break;
case 24:
nTemp = 2;
break;
default:
nTemp = 0; // -Wall set a default...
break;
}
ImplWriteTag( PhotometricInterpretation, 3, 1, nTemp );
mnBitmapPos = mpOStm->Tell();
ImplWriteTag( StripOffsets, 4, 1, 0 );
ImplWriteTag( SamplesPerPixel, 3, 1, ( mnBitsPerPixel == 24 ) ? 3 : 1 );
ImplWriteTag( RowsPerStrip, 4, 1, mnHeight ); //0xffffffff );
mnStripByteCountPos = mpOStm->Tell();
ImplWriteTag( StripByteCounts, 4, 1, ( ( mnWidth * mnBitsPerPixel * mnHeight ) + 7 ) >> 3 );
mnXResPos = mpOStm->Tell();
ImplWriteTag( XResolution, 5, 1, 0 );
mnYResPos = mpOStm->Tell();
ImplWriteTag( YResolution, 5, 1, 0 );
if ( mnBitsPerPixel != 1 )
ImplWriteTag( PlanarConfiguration, 3, 1, 1 ); // ( RGB ORDER )
ImplWriteTag( ResolutionUnit, 3, 1, 2); // Resolution Unit is Inch
if ( ( mnBitsPerPixel == 4 ) || ( mnBitsPerPixel == 8 ) )
{
mnColors = mpAcc->GetPaletteEntryCount();
mnPalPos = mpOStm->Tell();
ImplWriteTag( ColorMap, 3, 3 * mnColors, 0 );
}
// and last we write zero to close the num dir entries list
mnLatestIfdPos = mpOStm->Tell();
*mpOStm << (sal_uInt32)0; // there are no more IFD
}
else
mbStatus = sal_False;
return mbStatus;
}
// ------------------------------------------------------------------------
void TIFFWriter::ImplWritePalette()
{
sal_uInt16 i;
sal_uLong nCurrentPos = mpOStm->Tell();
mpOStm->Seek( mnPalPos + 8 ); // the palette tag entry needs the offset
*mpOStm << static_cast<sal_uInt32>(nCurrentPos - mnStreamOfs); // to the palette colors
mpOStm->Seek( nCurrentPos );
for ( i = 0; i < mnColors; i++ )
{
const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
*mpOStm << (sal_uInt16)( rColor.GetRed() << 8 );
}
for ( i = 0; i < mnColors; i++ )
{
const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
*mpOStm << (sal_uInt16)( rColor.GetGreen() << 8 );
}
for ( i = 0; i < mnColors; i++ )
{
const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
*mpOStm << (sal_uInt16)( rColor.GetBlue() << 8 );
}
}
// ------------------------------------------------------------------------
sal_Bool TIFFWriter::ImplWriteBody()
{
sal_uInt8 nTemp = 0;
sal_uInt8 nShift;
sal_uLong j, x, y;
sal_uLong nGfxBegin = mpOStm->Tell();
mpOStm->Seek( mnBitmapPos + 8 ); // the strip offset tag entry needs the offset
*mpOStm << static_cast<sal_uInt32>(nGfxBegin - mnStreamOfs); // to the bitmap data
mpOStm->Seek( nGfxBegin );
StartCompression();
switch( mnBitsPerPixel )
{
case 24 :
{
for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
{
ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
for ( x = 0; x < mnWidth; x++ )
{
const BitmapColor& rColor = mpAcc->GetPixel( y, x );
Compress( rColor.GetRed() );
Compress( rColor.GetGreen() );
Compress( rColor.GetBlue() );
}
}
}
break;
case 8 :
{
for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
{
ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
for ( x = 0; x < mnWidth; x++ )
{
Compress( mpAcc->GetPixelIndex( y, x ) );
}
}
}
break;
case 4 :
{
for ( nShift = 0, y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
{
ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
for ( x = 0; x < mnWidth; x++, nShift++ )
{
if (!( nShift & 1 ))
nTemp = ( mpAcc->GetPixelIndex( y, x ) << 4 );
else
Compress( (sal_uInt8)( nTemp | ( mpAcc->GetPixelIndex( y, x ) & 0xf ) ) );
}
if ( nShift & 1 )
Compress( nTemp );
}
}
break;
case 1 :
{
j = 1;
for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
{
ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
for ( x = 0; x < mnWidth; x++)
{
j <<= 1;
j |= ( ( ~mpAcc->GetPixelIndex( y, x ) ) & 1 );
if ( j & 0x100 )
{
Compress( (sal_uInt8)j );
j = 1;
}
}
if ( j != 1 )
{
Compress( (sal_uInt8)(j << ( ( ( x & 7) ^ 7 ) + 1 ) ) );
j = 1;
}
}
}
break;
default:
{
mbStatus = sal_False;
}
break;
}
EndCompression();
if ( mnStripByteCountPos && mbStatus )
{
sal_uLong nGfxEnd = mpOStm->Tell();
mpOStm->Seek( mnStripByteCountPos + 8 );
*mpOStm << static_cast<sal_uInt32>( nGfxEnd - nGfxBegin ); // mnStripByteCountPos needs the size of the compression data
mpOStm->Seek( nGfxEnd );
}
return mbStatus;
}
// ------------------------------------------------------------------------
void TIFFWriter::ImplWriteResolution( sal_uLong nStreamPos, sal_uInt32 nResolutionUnit )
{
sal_uLong nCurrentPos = mpOStm->Tell();
mpOStm->Seek( nStreamPos + 8 );
*mpOStm << (sal_uInt32)nCurrentPos - mnStreamOfs;
mpOStm->Seek( nCurrentPos );
*mpOStm << (sal_uInt32)1;
*mpOStm << nResolutionUnit;
}
// ------------------------------------------------------------------------
void TIFFWriter::ImplWriteTag( sal_uInt16 nTagID, sal_uInt16 nDataType, sal_uInt32 nNumberOfItems, sal_uInt32 nValue)
{
mnTagCount++;
*mpOStm << nTagID;
*mpOStm << nDataType;
*mpOStm << nNumberOfItems;
if ( nDataType == 3 )
nValue <<=16; // in Big Endian Mode WORDS needed to be shifted to a DWORD
*mpOStm << nValue;
}
// ------------------------------------------------------------------------
inline void TIFFWriter::WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen )
{
dwShift |= ( nCode << ( nOffset - nCodeLen ) );
nOffset -= nCodeLen;
while ( nOffset < 24 )
{
*mpOStm << (sal_uInt8)( dwShift >> 24 );
dwShift <<= 8;
nOffset += 8;
}
if ( nCode == 257 && nOffset != 32 )
{
*mpOStm << (sal_uInt8)( dwShift >> 24 );
}
}
// ------------------------------------------------------------------------
void TIFFWriter::StartCompression()
{
sal_uInt16 i;
nDataSize = 8;
nClearCode = 1 << nDataSize;
nEOICode = nClearCode + 1;
nTableSize = nEOICode + 1;
nCodeSize = nDataSize + 1;
nOffset = 32; // anzahl freier bits in dwShift
dwShift = 0;
pTable = new TIFFLZWCTreeNode[ 4096 ];
for ( i = 0; i < 4096; i++)
{
pTable[ i ].pBrother = pTable[ i ].pFirstChild = NULL;
pTable[ i ].nValue = (sal_uInt8)( pTable[ i ].nCode = i );
}
pPrefix = NULL;
WriteBits( nClearCode, nCodeSize );
}
// ------------------------------------------------------------------------
void TIFFWriter::Compress( sal_uInt8 nCompThis )
{
TIFFLZWCTreeNode* p;
sal_uInt16 i;
sal_uInt8 nV;
if( !pPrefix )
{
pPrefix = pTable + nCompThis;
}
else
{
nV = nCompThis;
for( p = pPrefix->pFirstChild; p != NULL; p = p->pBrother )
{
if ( p->nValue == nV )
break;
}
if( p )
pPrefix = p;
else
{
WriteBits( pPrefix->nCode, nCodeSize );
if ( nTableSize == 409 )
{
WriteBits( nClearCode, nCodeSize );
for ( i = 0; i < nClearCode; i++ )
pTable[ i ].pFirstChild = NULL;
nCodeSize = nDataSize + 1;
nTableSize = nEOICode + 1;
}
else
{
if( nTableSize == (sal_uInt16)( ( 1 << nCodeSize ) - 1 ) )
nCodeSize++;
p = pTable + ( nTableSize++ );
p->pBrother = pPrefix->pFirstChild;
pPrefix->pFirstChild = p;
p->nValue = nV;
p->pFirstChild = NULL;
}
pPrefix = pTable + nV;
}
}
}
// ------------------------------------------------------------------------
void TIFFWriter::EndCompression()
{
if( pPrefix )
WriteBits( pPrefix->nCode, nCodeSize );
WriteBits( nEOICode, nCodeSize );
delete[] pTable;
}
// ------------------------------------------------------------------------
// ---------------------
// - exported function -
// ---------------------
extern "C" sal_Bool __LOADONCALLAPI GraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pFilterConfigItem, sal_Bool )
{
return TIFFWriter().WriteTIFF( rGraphic, rStream, pFilterConfigItem );
}