diff options
Diffstat (limited to 'vcl/source/gdi/gdimtf.cxx')
-rw-r--r-- | vcl/source/gdi/gdimtf.cxx | 3195 |
1 files changed, 3195 insertions, 0 deletions
diff --git a/vcl/source/gdi/gdimtf.cxx b/vcl/source/gdi/gdimtf.cxx new file mode 100644 index 000000000000..1e99c5c3c04f --- /dev/null +++ b/vcl/source/gdi/gdimtf.cxx @@ -0,0 +1,3195 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <vos/macros.hxx> +#include <rtl/crc.h> +#include <tools/stream.hxx> +#include <tools/vcompat.hxx> +#include <vcl/metaact.hxx> +#include <vcl/salbtype.hxx> +#include <vcl/outdev.hxx> +#include <vcl/window.hxx> +#ifndef _SV_CVTSVM_HXX +#include <vcl/cvtsvm.hxx> +#endif +#include <vcl/virdev.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/graphictools.hxx> + +// ----------- +// - Defines - +// ----------- + +#define GAMMA( _def_cVal, _def_InvGamma ) ((sal_uInt8)MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0L,255L)) + +// -------------------------- +// - Color exchange structs - +// -------------------------- + +struct ImplColAdjustParam +{ + sal_uInt8* pMapR; + sal_uInt8* pMapG; + sal_uInt8* pMapB; +}; + +struct ImplBmpAdjustParam +{ + short nLuminancePercent; + short nContrastPercent; + short nChannelRPercent; + short nChannelGPercent; + short nChannelBPercent; + double fGamma; + sal_Bool bInvert; +}; + +// ----------------------------------------------------------------------------- + +struct ImplColConvertParam +{ + MtfConversion eConversion; +}; + +struct ImplBmpConvertParam +{ + BmpConversion eConversion; +}; + +// ----------------------------------------------------------------------------- + +struct ImplColMonoParam +{ + Color aColor; +}; + +struct ImplBmpMonoParam +{ + Color aColor; +}; + +// ----------------------------------------------------------------------------- + +struct ImplColReplaceParam +{ + sal_uLong* pMinR; + sal_uLong* pMaxR; + sal_uLong* pMinG; + sal_uLong* pMaxG; + sal_uLong* pMinB; + sal_uLong* pMaxB; + const Color* pDstCols; + sal_uLong nCount; +}; + +struct ImplBmpReplaceParam +{ + const Color* pSrcCols; + const Color* pDstCols; + sal_uLong nCount; + const sal_uLong* pTols; +}; + + +// --------- +// - Label - +// --------- + +struct ImpLabel +{ + String aLabelName; + sal_uLong nActionPos; + + ImpLabel( const String& rLabelName, sal_uLong _nActionPos ) : + aLabelName( rLabelName ), + nActionPos( _nActionPos ) {} +}; + +// ------------- +// - LabelList - +// ------------- + +class ImpLabelList : private List +{ +public: + + ImpLabelList() : List( 8, 4, 4 ) {} + ImpLabelList( const ImpLabelList& rList ); + ~ImpLabelList(); + + void ImplInsert( ImpLabel* p ) { Insert( p, LIST_APPEND ); } + ImpLabel* ImplRemove( sal_uLong nPos ) { return (ImpLabel*) Remove( nPos ); } + void ImplReplace( ImpLabel* p ) { Replace( (void*)p ); } + ImpLabel* ImplFirst() { return (ImpLabel*) First(); } + ImpLabel* ImplNext() { return (ImpLabel*) Next(); } + ImpLabel* ImplGetLabel( sal_uLong nPos ) const { return (ImpLabel*) GetObject( nPos ); } + sal_uLong ImplGetLabelPos( const String& rLabelName ); + sal_uLong ImplCount() const { return Count(); } +}; + +// ------------------------------------------------------------------------ + +ImpLabelList::ImpLabelList( const ImpLabelList& rList ) : + List( rList ) +{ + for( ImpLabel* pLabel = ImplFirst(); pLabel; pLabel = ImplNext() ) + ImplReplace( new ImpLabel( *pLabel ) ); +} + +// ------------------------------------------------------------------------ + +ImpLabelList::~ImpLabelList() +{ + for( ImpLabel* pLabel = ImplFirst(); pLabel; pLabel = ImplNext() ) + delete pLabel; +} + +// ------------------------------------------------------------------------ + +sal_uLong ImpLabelList::ImplGetLabelPos( const String& rLabelName ) +{ + sal_uLong nLabelPos = METAFILE_LABEL_NOTFOUND; + + for( ImpLabel* pLabel = ImplFirst(); pLabel; pLabel = ImplNext() ) + { + if ( rLabelName == pLabel->aLabelName ) + { + nLabelPos = GetCurPos(); + break; + } + } + + return nLabelPos; +} + +// --------------- +// - GDIMetaFile - +// --------------- + +GDIMetaFile::GDIMetaFile() : + List ( 0x3EFF, 64, 64 ), + aPrefSize ( 1, 1 ), + pPrev ( NULL ), + pNext ( NULL ), + pOutDev ( NULL ), + pLabelList ( NULL ), + bPause ( sal_False ), + bRecord ( sal_False ) +{ +} + +// ------------------------------------------------------------------------ + +GDIMetaFile::GDIMetaFile( const GDIMetaFile& rMtf ) : + List ( rMtf ), + aPrefMapMode ( rMtf.aPrefMapMode ), + aPrefSize ( rMtf.aPrefSize ), + aHookHdlLink ( rMtf.aHookHdlLink ), + pPrev ( rMtf.pPrev ), + pNext ( rMtf.pNext ), + pOutDev ( NULL ), + bPause ( sal_False ), + bRecord ( sal_False ) +{ + // RefCount der MetaActions erhoehen + for( void* pAct = First(); pAct; pAct = Next() ) + ( (MetaAction*) pAct )->Duplicate(); + + if( rMtf.pLabelList ) + pLabelList = new ImpLabelList( *rMtf.pLabelList ); + else + pLabelList = NULL; + + if( rMtf.bRecord ) + { + Record( rMtf.pOutDev ); + + if ( rMtf.bPause ) + Pause( sal_True ); + } +} + +// ------------------------------------------------------------------------ + +GDIMetaFile::~GDIMetaFile() +{ + Clear(); +} + +// ------------------------------------------------------------------------ + +GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf ) +{ + if( this != &rMtf ) + { + Clear(); + + List::operator=( rMtf ); + + // RefCount der MetaActions erhoehen + for( void* pAct = First(); pAct; pAct = Next() ) + ( (MetaAction*) pAct )->Duplicate(); + + if( rMtf.pLabelList ) + pLabelList = new ImpLabelList( *rMtf.pLabelList ); + else + pLabelList = NULL; + + aPrefMapMode = rMtf.aPrefMapMode; + aPrefSize = rMtf.aPrefSize; + aHookHdlLink = rMtf.aHookHdlLink; + pPrev = rMtf.pPrev; + pNext = rMtf.pNext; + pOutDev = NULL; + bPause = sal_False; + bRecord = sal_False; + + if( rMtf.bRecord ) + { + Record( rMtf.pOutDev ); + + if( rMtf.bPause ) + Pause( sal_True ); + } + } + + return *this; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::operator==( const GDIMetaFile& rMtf ) const +{ + const sal_uLong nObjCount = Count(); + sal_Bool bRet = sal_False; + + if( this == &rMtf ) + bRet = sal_True; + else if( rMtf.GetActionCount() == nObjCount && + rMtf.GetPrefSize() == aPrefSize && + rMtf.GetPrefMapMode() == aPrefMapMode ) + { + bRet = sal_True; + + for( sal_uLong n = 0UL; n < nObjCount; n++ ) + { + if( GetObject( n ) != rMtf.GetObject( n ) ) + { + bRet = sal_False; + break; + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::IsEqual( const GDIMetaFile& rMtf ) const +{ + const sal_uLong nObjCount = Count(); + sal_Bool bRet = sal_False; + + if( this == &rMtf ) + bRet = sal_True; + else if( rMtf.GetActionCount() == nObjCount && + rMtf.GetPrefSize() == aPrefSize && + rMtf.GetPrefMapMode() == aPrefMapMode ) + { + bRet = sal_True; + + for( sal_uLong n = 0UL; n < nObjCount; n++ ) + { + if(!((MetaAction*)GetObject( n ))->IsEqual(*((MetaAction*)rMtf.GetObject( n )))) + { + bRet = sal_False; + break; + } + } + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Clear() +{ + if( bRecord ) + Stop(); + + for( void* pAct = First(); pAct; pAct = Next() ) + ( (MetaAction*) pAct )->Delete(); + + List::Clear(); + + delete pLabelList; + pLabelList = NULL; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Linker( OutputDevice* pOut, sal_Bool bLink ) +{ + if( bLink ) + { + pNext = NULL; + pPrev = pOut->GetConnectMetaFile(); + pOut->SetConnectMetaFile( this ); + + if( pPrev ) + pPrev->pNext = this; + } + else + { + if( pNext ) + { + pNext->pPrev = pPrev; + + if( pPrev ) + pPrev->pNext = pNext; + } + else + { + if( pPrev ) + pPrev->pNext = NULL; + + pOut->SetConnectMetaFile( pPrev ); + } + + pPrev = NULL; + pNext = NULL; + } +} + +// ------------------------------------------------------------------------ + +long GDIMetaFile::Hook() +{ + return aHookHdlLink.Call( this ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Record( OutputDevice* pOut ) +{ + if( bRecord ) + Stop(); + + Last(); + pOutDev = pOut; + bRecord = sal_True; + Linker( pOut, sal_True ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Play( GDIMetaFile& rMtf, sal_uLong nPos ) +{ + if ( !bRecord && !rMtf.bRecord ) + { + MetaAction* pAction = GetCurAction(); + const sal_uLong nObjCount = Count(); + + if( nPos > nObjCount ) + nPos = nObjCount; + + for( sal_uLong nCurPos = GetCurPos(); nCurPos < nPos; nCurPos++ ) + { + if( !Hook() ) + { + pAction->Duplicate(); + rMtf.AddAction( pAction ); + } + + pAction = (MetaAction*) Next(); + } + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Play( OutputDevice* pOut, sal_uLong nPos ) +{ + if( !bRecord ) + { + MetaAction* pAction = GetCurAction(); + const sal_uLong nObjCount = Count(); + sal_uLong i = 0, nSyncCount = ( pOut->GetOutDevType() == OUTDEV_WINDOW ) ? 0x000000ff : 0xffffffff; + + if( nPos > nObjCount ) + nPos = nObjCount; + + // #i23407# Set backwards-compatible text language and layout mode + // This is necessary, since old metafiles don't even know of these + // recent add-ons. Newer metafiles must of course explicitely set + // those states. + pOut->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE ); + pOut->SetLayoutMode( 0 ); + pOut->SetDigitLanguage( 0 ); + + for( sal_uLong nCurPos = GetCurPos(); nCurPos < nPos; nCurPos++ ) + { + if( !Hook() ) + { + pAction->Execute( pOut ); + + // flush output from time to time + if( i++ > nSyncCount ) + ( (Window*) pOut )->Flush(), i = 0; + } + + pAction = (MetaAction*) Next(); + } + + pOut->Pop(); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Play( OutputDevice* pOut, const Point& rPos, + const Size& rSize, sal_uLong nPos ) +{ + Region aDrawClipRegion; + MapMode aDrawMap( GetPrefMapMode() ); + Size aDestSize( pOut->LogicToPixel( rSize ) ); + + if( aDestSize.Width() && aDestSize.Height() ) + { + Size aTmpPrefSize( pOut->LogicToPixel( GetPrefSize(), aDrawMap ) ); + GDIMetaFile* pMtf = pOut->GetConnectMetaFile(); + + if( !aTmpPrefSize.Width() ) + aTmpPrefSize.Width() = aDestSize.Width(); + + if( !aTmpPrefSize.Height() ) + aTmpPrefSize.Height() = aDestSize.Height(); + + Fraction aScaleX( aDestSize.Width(), aTmpPrefSize.Width() ); + Fraction aScaleY( aDestSize.Height(), aTmpPrefSize.Height() ); + + aScaleX *= aDrawMap.GetScaleX(); aDrawMap.SetScaleX( aScaleX ); + aScaleY *= aDrawMap.GetScaleY(); aDrawMap.SetScaleY( aScaleY ); + + // #i47260# Convert logical output position to offset within + // the metafile's mapmode. Therefore, disable pixel offset on + // outdev, it's inverse mnOutOffLogicX/Y is calculated for a + // different mapmode (the one currently set on pOut, that is) + // - thus, aDrawMap's origin would generally be wrong. And + // even _if_ aDrawMap is similar to pOutDev's current mapmode, + // it's _still_ undesirable to have pixel offset unequal zero, + // because one would still get round-off errors (the + // round-trip error for LogicToPixel( PixelToLogic() ) was the + // reason for having pixel offset in the first place). + const Size& rOldOffset( pOut->GetPixelOffset() ); + const Size aEmptySize; + pOut->SetPixelOffset( aEmptySize ); + aDrawMap.SetOrigin( pOut->PixelToLogic( pOut->LogicToPixel( rPos ), aDrawMap ) ); + pOut->SetPixelOffset( rOldOffset ); + + pOut->Push(); + + if ( pMtf && pMtf->IsRecord() && ( pOut->GetOutDevType() != OUTDEV_PRINTER ) ) + pOut->SetRelativeMapMode( aDrawMap ); + else + pOut->SetMapMode( aDrawMap ); + + // #i23407# Set backwards-compatible text language and layout mode + // This is necessary, since old metafiles don't even know of these + // recent add-ons. Newer metafiles must of course explicitely set + // those states. + pOut->SetLayoutMode( 0 ); + pOut->SetDigitLanguage( 0 ); + + Play( pOut, nPos ); + + pOut->Pop(); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Pause( sal_Bool _bPause ) +{ + if( bRecord ) + { + if( _bPause ) + { + if( !bPause ) + Linker( pOutDev, sal_False ); + } + else + { + if( bPause ) + Linker( pOutDev, sal_True ); + } + + bPause = _bPause; + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Stop() +{ + if( bRecord ) + { + bRecord = sal_False; + + if( !bPause ) + Linker( pOutDev, sal_False ); + else + bPause = sal_False; + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindStart() +{ + if( !bRecord ) + First(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindEnd() +{ + if( !bRecord ) + Last(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Wind( sal_uLong nActionPos ) +{ + if( !bRecord ) + Seek( nActionPos ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindPrev() +{ + if( !bRecord ) + Prev(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::WindNext() +{ + if( !bRecord ) + Next(); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::AddAction( MetaAction* pAction ) +{ + Insert( pAction, LIST_APPEND ); + + if( pPrev ) + { + pAction->Duplicate(); + pPrev->AddAction( pAction ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::AddAction( MetaAction* pAction, sal_uLong nPos ) +{ + Insert( pAction, nPos ); + + if( pPrev ) + { + pAction->Duplicate(); + pPrev->AddAction( pAction, nPos ); + } +} + +// ------------------------------------------------------------------------ + +// @since #110496# +void GDIMetaFile::RemoveAction( sal_uLong nPos ) +{ + Remove( nPos ); + + if( pPrev ) + pPrev->RemoveAction( nPos ); +} + +// ------------------------------------------------------------------------ + +MetaAction* GDIMetaFile::CopyAction( sal_uLong nPos ) const +{ + return ( (MetaAction*) GetObject( nPos ) )->Clone(); +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetActionPos( const String& rLabel ) +{ + ImpLabel* pLabel = NULL; + + if( pLabelList ) + pLabel = pLabelList->ImplGetLabel( pLabelList->ImplGetLabelPos( rLabel ) ); + else + pLabel = NULL; + + return( pLabel ? pLabel->nActionPos : METAFILE_LABEL_NOTFOUND ); +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::InsertLabel( const String& rLabel, sal_uLong nActionPos ) +{ + sal_Bool bRet = sal_False; + + if( !pLabelList ) + pLabelList = new ImpLabelList; + + if( pLabelList->ImplGetLabelPos( rLabel ) == METAFILE_LABEL_NOTFOUND ) + { + pLabelList->ImplInsert( new ImpLabel( rLabel, nActionPos ) ); + bRet = sal_True; + } + + return bRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::RemoveLabel( const String& rLabel ) +{ + if( pLabelList ) + { + const sal_uLong nLabelPos = pLabelList->ImplGetLabelPos( rLabel ); + + if( nLabelPos != METAFILE_LABEL_NOTFOUND ) + delete pLabelList->ImplRemove( nLabelPos ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::RenameLabel( const String& rLabel, const String& rNewLabel ) +{ + if( pLabelList ) + { + const sal_uLong nLabelPos = pLabelList->ImplGetLabelPos( rLabel ); + + if ( nLabelPos != METAFILE_LABEL_NOTFOUND ) + pLabelList->ImplGetLabel( nLabelPos )->aLabelName = rNewLabel; + } +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetLabelCount() const +{ + return( pLabelList ? pLabelList->ImplCount() : 0UL ); +} + +// ------------------------------------------------------------------------ + +String GDIMetaFile::GetLabel( sal_uLong nLabel ) +{ + String aString; + + if( pLabelList ) + { + const ImpLabel* pLabel = pLabelList->ImplGetLabel( nLabel ); + + if( pLabel ) + aString = pLabel->aLabelName; + } + + return aString; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::SaveStatus() +{ + if ( bRecord ) + { + if ( bPause ) + Linker( pOutDev, sal_True ); + + AddAction( new MetaLineColorAction( pOutDev->GetLineColor(), + pOutDev->IsLineColor() ) ); + AddAction( new MetaFillColorAction( pOutDev->GetFillColor(), + pOutDev->IsFillColor() ) ); + AddAction( new MetaFontAction( pOutDev->GetFont() ) ); + AddAction( new MetaTextColorAction( pOutDev->GetTextColor() ) ); + AddAction( new MetaTextFillColorAction( pOutDev->GetTextFillColor(), + pOutDev->IsTextFillColor() ) ); + AddAction( new MetaTextLineColorAction( pOutDev->GetTextLineColor(), + pOutDev->IsTextLineColor() ) ); + AddAction( new MetaOverlineColorAction( pOutDev->GetOverlineColor(), + pOutDev->IsOverlineColor() ) ); + AddAction( new MetaTextAlignAction( pOutDev->GetTextAlign() ) ); + AddAction( new MetaRasterOpAction( pOutDev->GetRasterOp() ) ); + AddAction( new MetaMapModeAction( pOutDev->GetMapMode() ) ); + AddAction( new MetaClipRegionAction( pOutDev->GetClipRegion(), + pOutDev->IsClipRegion() ) ); + + if ( bPause ) + Linker( pOutDev, sal_False ); + + return sal_True; + } + else + return sal_False; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::Mirror( sal_uLong nMirrorFlags ) +{ + const Size aOldPrefSize( GetPrefSize() ); + long nMoveX, nMoveY; + double fScaleX, fScaleY; + sal_Bool bRet; + + if( nMirrorFlags & MTF_MIRROR_HORZ ) + nMoveX = VOS_ABS( aOldPrefSize.Width() ) - 1, fScaleX = -1.0; + else + nMoveX = 0, fScaleX = 1.0; + + if( nMirrorFlags & MTF_MIRROR_VERT ) + nMoveY = VOS_ABS( aOldPrefSize.Height() ) - 1, fScaleY = -1.0; + else + nMoveY = 0, fScaleY = 1.0; + + if( ( fScaleX != 1.0 ) || ( fScaleY != 1.0 ) ) + { + Scale( fScaleX, fScaleY ); + Move( nMoveX, nMoveY ); + SetPrefSize( aOldPrefSize ); + bRet = sal_True; + } + else + bRet = sal_False; + + return bRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Move( long nX, long nY ) +{ + const Size aBaseOffset( nX, nY ); + Size aOffset( aBaseOffset ); + VirtualDevice aMapVDev; + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + const long nType = pAct->GetType(); + MetaAction* pModAct; + + if( pAct->GetRefCount() > 1 ) + { + Replace( pModAct = pAct->Clone(), GetCurPos() ); + pAct->Delete(); + } + else + pModAct = pAct; + + if( ( META_MAPMODE_ACTION == nType ) || + ( META_PUSH_ACTION == nType ) || + ( META_POP_ACTION == nType ) ) + { + pModAct->Execute( &aMapVDev ); + aOffset = aMapVDev.LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev.GetMapMode() ); + } + + pModAct->Move( aOffset.Width(), aOffset.Height() ); + } +} + +void GDIMetaFile::Move( long nX, long nY, long nDPIX, long nDPIY ) +{ + const Size aBaseOffset( nX, nY ); + Size aOffset( aBaseOffset ); + VirtualDevice aMapVDev; + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetReferenceDevice( nDPIX, nDPIY ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + const long nType = pAct->GetType(); + MetaAction* pModAct; + + if( pAct->GetRefCount() > 1 ) + { + Replace( pModAct = pAct->Clone(), GetCurPos() ); + pAct->Delete(); + } + else + pModAct = pAct; + + if( ( META_MAPMODE_ACTION == nType ) || + ( META_PUSH_ACTION == nType ) || + ( META_POP_ACTION == nType ) ) + { + pModAct->Execute( &aMapVDev ); + if( aMapVDev.GetMapMode().GetMapUnit() == MAP_PIXEL ) + { + aOffset = aMapVDev.LogicToPixel( aBaseOffset, GetPrefMapMode() ); + MapMode aMap( aMapVDev.GetMapMode() ); + aOffset.Width() = static_cast<long>(aOffset.Width() * (double)aMap.GetScaleX()); + aOffset.Height() = static_cast<long>(aOffset.Height() * (double)aMap.GetScaleY()); + } + else + aOffset = aMapVDev.LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev.GetMapMode() ); + } + + pModAct->Move( aOffset.Width(), aOffset.Height() ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Scale( double fScaleX, double fScaleY ) +{ + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + MetaAction* pModAct; + + if( pAct->GetRefCount() > 1 ) + { + Replace( pModAct = pAct->Clone(), GetCurPos() ); + pAct->Delete(); + } + else + pModAct = pAct; + + pModAct->Scale( fScaleX, fScaleY ); + } + + aPrefSize.Width() = FRound( aPrefSize.Width() * fScaleX ); + aPrefSize.Height() = FRound( aPrefSize.Height() * fScaleY ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Scale( const Fraction& rScaleX, const Fraction& rScaleY ) +{ + Scale( (double) rScaleX, (double) rScaleY ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Clip( const Rectangle& i_rClipRect ) +{ + Rectangle aCurRect( i_rClipRect ); + VirtualDevice aMapVDev; + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + for( MetaAction* pAct = (MetaAction*) First(); pAct; pAct = (MetaAction*) Next() ) + { + const long nType = pAct->GetType(); + + if( ( META_MAPMODE_ACTION == nType ) || + ( META_PUSH_ACTION == nType ) || + ( META_POP_ACTION == nType ) ) + { + pAct->Execute( &aMapVDev ); + aCurRect = aMapVDev.LogicToLogic( i_rClipRect, GetPrefMapMode(), aMapVDev.GetMapMode() ); + } + else if( nType == META_CLIPREGION_ACTION ) + { + MetaClipRegionAction* pOldAct = (MetaClipRegionAction*)pAct; + Region aNewReg( aCurRect ); + if( pOldAct->IsClipping() ) + aNewReg.Intersect( pOldAct->GetRegion() ); + MetaClipRegionAction* pNewAct = new MetaClipRegionAction( aNewReg, sal_True ); + Replace( pNewAct, GetCurPos() ); + pOldAct->Delete(); + } + } +} + +// ------------------------------------------------------------------------ + +Point GDIMetaFile::ImplGetRotatedPoint( const Point& rPt, const Point& rRotatePt, + const Size& rOffset, double fSin, double fCos ) +{ + const long nX = rPt.X() - rRotatePt.X(); + const long nY = rPt.Y() - rRotatePt.Y(); + + return Point( FRound( fCos * nX + fSin * nY ) + rRotatePt.X() + rOffset.Width(), + -FRound( fSin * nX - fCos * nY ) + rRotatePt.Y() + rOffset.Height() ); +} + +// ------------------------------------------------------------------------ + +Polygon GDIMetaFile::ImplGetRotatedPolygon( const Polygon& rPoly, const Point& rRotatePt, + const Size& rOffset, double fSin, double fCos ) +{ + Polygon aRet( rPoly ); + + aRet.Rotate( rRotatePt, fSin, fCos ); + aRet.Move( rOffset.Width(), rOffset.Height() ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +PolyPolygon GDIMetaFile::ImplGetRotatedPolyPolygon( const PolyPolygon& rPolyPoly, const Point& rRotatePt, + const Size& rOffset, double fSin, double fCos ) +{ + PolyPolygon aRet( rPolyPoly ); + + aRet.Rotate( rRotatePt, fSin, fCos ); + aRet.Move( rOffset.Width(), rOffset.Height() ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ImplAddGradientEx( GDIMetaFile& rMtf, + const OutputDevice& rMapDev, + const PolyPolygon& rPolyPoly, + const Gradient& rGrad ) +{ + // #105055# Generate comment, GradientEx and Gradient actions + // (within DrawGradient) + VirtualDevice aVDev( rMapDev, 0 ); + aVDev.EnableOutput( sal_False ); + GDIMetaFile aGradMtf; + + aGradMtf.Record( &aVDev ); + aVDev.DrawGradient( rPolyPoly, rGrad ); + aGradMtf.Stop(); + + int i, nAct( aGradMtf.GetActionCount() ); + for( i=0; i<nAct; ++i ) + { + MetaAction* pMetaAct = aGradMtf.GetAction(i); + pMetaAct->Duplicate(); + rMtf.AddAction( pMetaAct ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Rotate( long nAngle10 ) +{ + nAngle10 %= 3600L; + nAngle10 = ( nAngle10 < 0L ) ? ( 3599L + nAngle10 ) : nAngle10; + + if( nAngle10 ) + { + GDIMetaFile aMtf; + VirtualDevice aMapVDev; + const double fAngle = F_PI1800 * nAngle10; + const double fSin = sin( fAngle ); + const double fCos = cos( fAngle ); + Rectangle aRect=Rectangle( Point(), GetPrefSize() ); + Polygon aPoly( aRect ); + + aPoly.Rotate( Point(), fSin, fCos ); + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + const Rectangle aNewBound( aPoly.GetBoundRect() ); + + const Point aOrigin( GetPrefMapMode().GetOrigin().X(), GetPrefMapMode().GetOrigin().Y() ); + const Size aOffset( -aNewBound.Left(), -aNewBound.Top() ); + + Point aRotAnchor( aOrigin ); + Size aRotOffset( aOffset ); + + for( MetaAction* pAction = (MetaAction*) First(); pAction; pAction = (MetaAction*) Next() ) + { + const sal_uInt16 nActionType = pAction->GetType(); + + switch( nActionType ) + { + case( META_PIXEL_ACTION ): + { + MetaPixelAction* pAct = (MetaPixelAction*) pAction; + aMtf.AddAction( new MetaPixelAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetColor() ) ); + } + break; + + case( META_POINT_ACTION ): + { + MetaPointAction* pAct = (MetaPointAction*) pAction; + aMtf.AddAction( new MetaPointAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_LINE_ACTION ): + { + MetaLineAction* pAct = (MetaLineAction*) pAction; + aMtf.AddAction( new MetaLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + ImplGetRotatedPoint( pAct->GetEndPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetLineInfo() ) ); + } + break; + + case( META_RECT_ACTION ): + { + MetaRectAction* pAct = (MetaRectAction*) pAction; + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ROUNDRECT_ACTION ): + { + MetaRoundRectAction* pAct = (MetaRoundRectAction*) pAction; + const Polygon aRoundRectPoly( pAct->GetRect(), pAct->GetHorzRound(), pAct->GetVertRound() ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aRoundRectPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ELLIPSE_ACTION ): + { + MetaEllipseAction* pAct = (MetaEllipseAction*) pAction; + const Polygon aEllipsePoly( pAct->GetRect().Center(), pAct->GetRect().GetWidth() >> 1, pAct->GetRect().GetHeight() >> 1 ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aEllipsePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ARC_ACTION ): + { + MetaArcAction* pAct = (MetaArcAction*) pAction; + const Polygon aArcPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), POLY_ARC ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aArcPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_PIE_ACTION ): + { + MetaPieAction* pAct = (MetaPieAction*) pAction; + const Polygon aPiePoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), POLY_PIE ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aPiePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_CHORD_ACTION ): + { + MetaChordAction* pAct = (MetaChordAction*) pAction; + const Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), POLY_CHORD ); + + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aChordPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_POLYLINE_ACTION ): + { + MetaPolyLineAction* pAct = (MetaPolyLineAction*) pAction; + aMtf.AddAction( new MetaPolyLineAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->GetLineInfo() ) ); + } + break; + + case( META_POLYGON_ACTION ): + { + MetaPolygonAction* pAct = (MetaPolygonAction*) pAction; + aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_POLYPOLYGON_ACTION ): + { + MetaPolyPolygonAction* pAct = (MetaPolyPolygonAction*) pAction; + aMtf.AddAction( new MetaPolyPolygonAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_TEXT_ACTION ): + { + MetaTextAction* pAct = (MetaTextAction*) pAction; + aMtf.AddAction( new MetaTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) ); + } + break; + + case( META_TEXTARRAY_ACTION ): + { + MetaTextArrayAction* pAct = (MetaTextArrayAction*) pAction; + aMtf.AddAction( new MetaTextArrayAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetText(), pAct->GetDXArray(), pAct->GetIndex(), pAct->GetLen() ) ); + } + break; + + case( META_STRETCHTEXT_ACTION ): + { + MetaStretchTextAction* pAct = (MetaStretchTextAction*) pAction; + aMtf.AddAction( new MetaStretchTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetWidth(), pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) ); + } + break; + + case( META_TEXTLINE_ACTION ): + { + MetaTextLineAction* pAct = (MetaTextLineAction*) pAction; + aMtf.AddAction( new MetaTextLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetWidth(), pAct->GetStrikeout(), pAct->GetUnderline(), pAct->GetOverline() ) ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmap() ); + + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), + aBmpEx ) ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmap() ); + + aBmpEx.Crop( Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) ); + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmapEx() ); + + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + Polygon aBmpPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aBmpRect( aBmpPoly.GetBoundRect() ); + BitmapEx aBmpEx( pAct->GetBitmapEx() ); + + aBmpEx.Crop( Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) ); + aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) ); + + aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) ); + } + break; + + case( META_GRADIENT_ACTION ): + { + MetaGradientAction* pAct = (MetaGradientAction*) pAction; + + ImplAddGradientEx( aMtf, aMapVDev, + ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetGradient() ); + } + break; + + case( META_GRADIENTEX_ACTION ): + { + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + aMtf.AddAction( new MetaGradientExAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetGradient() ) ); + } + break; + + // #105055# Handle gradientex comment block correctly + case( META_COMMENT_ACTION ): + { + MetaCommentAction* pCommentAct = (MetaCommentAction*) pAction; + if( pCommentAct->GetComment().Equals( "XGRAD_SEQ_BEGIN" ) ) + { + int nBeginComments( 1 ); + pAction = (MetaAction*) Next(); + + // skip everything, except gradientex action + while( pAction ) + { + const sal_uInt16 nType = pAction->GetType(); + + if( META_GRADIENTEX_ACTION == nType ) + { + // Add rotated gradientex + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + ImplAddGradientEx( aMtf, aMapVDev, + ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetGradient() ); + } + else if( META_COMMENT_ACTION == nType) + { + MetaCommentAction* pAct = (MetaCommentAction*) pAction; + if( pAct->GetComment().Equals( "XGRAD_SEQ_END" ) ) + { + // handle nested blocks + --nBeginComments; + + // gradientex comment block: end reached, done. + if( !nBeginComments ) + break; + } + else if( pAct->GetComment().Equals( "XGRAD_SEQ_BEGIN" ) ) + { + // handle nested blocks + ++nBeginComments; + } + + } + + pAction = (MetaAction*) Next(); + } + } + else + { + sal_Bool bPathStroke = pCommentAct->GetComment().Equals( "XPATHSTROKE_SEQ_BEGIN" ); + if ( bPathStroke || pCommentAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) + { + if ( pCommentAct->GetDataSize() ) + { + SvMemoryStream aMemStm( (void*)pCommentAct->GetData(), pCommentAct->GetDataSize(), STREAM_READ ); + SvMemoryStream aDest; + if ( bPathStroke ) + { + SvtGraphicStroke aStroke; + aMemStm >> aStroke; + Polygon aPath; + aStroke.getPath( aPath ); + aStroke.setPath( ImplGetRotatedPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) ); + aDest << aStroke; + aMtf.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN", 0, + static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) ); + } + else + { + SvtGraphicFill aFill; + aMemStm >> aFill; + PolyPolygon aPath; + aFill.getPath( aPath ); + aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) ); + aDest << aFill; + aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0, + static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) ); + } + } + } + else if ( pCommentAct->GetComment().Equals( "XPATHSTROKE_SEQ_END" ) + || pCommentAct->GetComment().Equals( "XPATHFILL_SEQ_END" ) ) + { + pAction->Execute( &aMapVDev ); + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + } + } + break; + + case( META_HATCH_ACTION ): + { + MetaHatchAction* pAct = (MetaHatchAction*) pAction; + Hatch aHatch( pAct->GetHatch() ); + + aHatch.SetAngle( aHatch.GetAngle() + (sal_uInt16) nAngle10 ); + aMtf.AddAction( new MetaHatchAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + aHatch ) ); + } + break; + + case( META_TRANSPARENT_ACTION ): + { + MetaTransparentAction* pAct = (MetaTransparentAction*) pAction; + aMtf.AddAction( new MetaTransparentAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), + pAct->GetTransparence() ) ); + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + MetaFloatTransparentAction* pAct = (MetaFloatTransparentAction*) pAction; + GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() ); + Polygon aMtfPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aMtfRect( aMtfPoly.GetBoundRect() ); + + aTransMtf.Rotate( nAngle10 ); + aMtf.AddAction( new MetaFloatTransparentAction( aTransMtf, aMtfRect.TopLeft(), aMtfRect.GetSize(), + pAct->GetGradient() ) ); + } + break; + + case( META_EPS_ACTION ): + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + GDIMetaFile aEPSMtf( pAct->GetSubstitute() ); + Polygon aEPSPoly( ImplGetRotatedPolygon( Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) ); + Rectangle aEPSRect( aEPSPoly.GetBoundRect() ); + + aEPSMtf.Rotate( nAngle10 ); + aMtf.AddAction( new MetaEPSAction( aEPSRect.TopLeft(), aEPSRect.GetSize(), + pAct->GetLink(), aEPSMtf ) ); + } + break; + + case( META_CLIPREGION_ACTION ): + { + MetaClipRegionAction* pAct = (MetaClipRegionAction*) pAction; + + if( pAct->IsClipping() && pAct->GetRegion().HasPolyPolygon() ) + aMtf.AddAction( new MetaClipRegionAction( Region( ImplGetRotatedPolyPolygon( pAct->GetRegion().GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ), sal_True ) ); + else + { + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + } + break; + + case( META_ISECTRECTCLIPREGION_ACTION ): + { + MetaISectRectClipRegionAction* pAct = (MetaISectRectClipRegionAction*) pAction; + aMtf.AddAction( new MetaISectRegionClipRegionAction( ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ) ) ); + } + break; + + case( META_ISECTREGIONCLIPREGION_ACTION ): + { + MetaISectRegionClipRegionAction* pAct = (MetaISectRegionClipRegionAction*) pAction; + const Region& rRegion = pAct->GetRegion(); + + if( rRegion.HasPolyPolygon() ) + aMtf.AddAction( new MetaISectRegionClipRegionAction( Region( ImplGetRotatedPolyPolygon( rRegion.GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ) ); + else + { + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + } + break; + + case( META_REFPOINT_ACTION ): + { + MetaRefPointAction* pAct = (MetaRefPointAction*) pAction; + aMtf.AddAction( new MetaRefPointAction( ImplGetRotatedPoint( pAct->GetRefPoint(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->IsSetting() ) ); + } + break; + + case( META_FONT_ACTION ): + { + MetaFontAction* pAct = (MetaFontAction*) pAction; + Font aFont( pAct->GetFont() ); + + aFont.SetOrientation( aFont.GetOrientation() + (sal_uInt16) nAngle10 ); + aMtf.AddAction( new MetaFontAction( aFont ) ); + } + break; + + case( META_BMP_ACTION ): + case( META_BMPEX_ACTION ): + case( META_MASK_ACTION ): + case( META_MASKSCALE_ACTION ): + case( META_MASKSCALEPART_ACTION ): + case( META_WALLPAPER_ACTION ): + case( META_TEXTRECT_ACTION ): + case( META_MOVECLIPREGION_ACTION ): + { + DBG_ERROR( "GDIMetaFile::Rotate(): unsupported action" ); + } + break; + + case( META_RENDERGRAPHIC_ACTION ): + { + OSL_TRACE( "Rotate not supported for RenderGraphic MetaActions yet" ); + + pAction->Duplicate(); + aMtf.AddAction( pAction ); + } + break; + + default: + { + pAction->Execute( &aMapVDev ); + pAction->Duplicate(); + aMtf.AddAction( pAction ); + + // update rotation point and offset, if necessary + if( ( META_MAPMODE_ACTION == nActionType ) || + ( META_PUSH_ACTION == nActionType ) || + ( META_POP_ACTION == nActionType ) ) + { + aRotAnchor = aMapVDev.LogicToLogic( aOrigin, aPrefMapMode, aMapVDev.GetMapMode() ); + aRotOffset = aMapVDev.LogicToLogic( aOffset, aPrefMapMode, aMapVDev.GetMapMode() ); + } + } + break; + } + } + + aMtf.aPrefMapMode = aPrefMapMode; + aMtf.aPrefSize = aNewBound.GetSize(); + + *this = aMtf; + } +} + +// ------------------------------------------------------------------------ + +static void ImplActionBounds( Rectangle& o_rOutBounds, + const Rectangle& i_rInBounds, + const std::vector<Rectangle>& i_rClipStack ) +{ + Rectangle aBounds( i_rInBounds ); + if( ! i_rInBounds.IsEmpty() && ! i_rClipStack.empty() && ! i_rClipStack.back().IsEmpty() ) + aBounds.Intersection( i_rClipStack.back() ); + if( ! aBounds.IsEmpty() ) + { + if( ! o_rOutBounds.IsEmpty() ) + o_rOutBounds.Union( aBounds ); + else + o_rOutBounds = aBounds; + } +} + +Rectangle GDIMetaFile::GetBoundRect( OutputDevice& i_rReference ) +{ + GDIMetaFile aMtf; + VirtualDevice aMapVDev( i_rReference ); + + aMapVDev.EnableOutput( sal_False ); + aMapVDev.SetMapMode( GetPrefMapMode() ); + + std::vector<Rectangle> aClipStack( 1, Rectangle() ); + std::vector<sal_uInt16> aPushFlagStack; + + Rectangle aBound; + + for( MetaAction* pAction = (MetaAction*) First(); pAction; pAction = (MetaAction*) Next() ) + { + const sal_uInt16 nActionType = pAction->GetType(); + + switch( nActionType ) + { + case( META_PIXEL_ACTION ): + { + MetaPixelAction* pAct = (MetaPixelAction*) pAction; + ImplActionBounds( aBound, + Rectangle( aMapVDev.LogicToLogic( pAct->GetPoint(), aMapVDev.GetMapMode(), GetPrefMapMode() ), + aMapVDev.PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ), + aClipStack ); + } + break; + + case( META_POINT_ACTION ): + { + MetaPointAction* pAct = (MetaPointAction*) pAction; + ImplActionBounds( aBound, + Rectangle( aMapVDev.LogicToLogic( pAct->GetPoint(), aMapVDev.GetMapMode(), GetPrefMapMode() ), + aMapVDev.PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ), + aClipStack ); + } + break; + + case( META_LINE_ACTION ): + { + MetaLineAction* pAct = (MetaLineAction*) pAction; + Point aP1( pAct->GetStartPoint() ), aP2( pAct->GetEndPoint() ); + Rectangle aRect( aP1, aP2 ); + aRect.Justify(); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_RECT_ACTION ): + { + MetaRectAction* pAct = (MetaRectAction*) pAction; + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_ROUNDRECT_ACTION ): + { + MetaRoundRectAction* pAct = (MetaRoundRectAction*) pAction; + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_ELLIPSE_ACTION ): + { + MetaEllipseAction* pAct = (MetaEllipseAction*) pAction; + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_ARC_ACTION ): + { + MetaArcAction* pAct = (MetaArcAction*) pAction; + // FIXME: this is imprecise + // e.g. for small arcs the whole rectangle is WAY too large + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_PIE_ACTION ): + { + MetaPieAction* pAct = (MetaPieAction*) pAction; + // FIXME: this is imprecise + // e.g. for small arcs the whole rectangle is WAY too large + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_CHORD_ACTION ): + { + MetaChordAction* pAct = (MetaChordAction*) pAction; + // FIXME: this is imprecise + // e.g. for small arcs the whole rectangle is WAY too large + ImplActionBounds( aBound, aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_POLYLINE_ACTION ): + { + MetaPolyLineAction* pAct = (MetaPolyLineAction*) pAction; + Rectangle aRect( pAct->GetPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_POLYGON_ACTION ): + { + MetaPolygonAction* pAct = (MetaPolygonAction*) pAction; + Rectangle aRect( pAct->GetPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_POLYPOLYGON_ACTION ): + { + MetaPolyPolygonAction* pAct = (MetaPolyPolygonAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXT_ACTION ): + { + MetaTextAction* pAct = (MetaTextAction*) pAction; + Rectangle aRect; + // hdu said base = index + aMapVDev.GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen() ); + Point aPt( pAct->GetPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXTARRAY_ACTION ): + { + MetaTextArrayAction* pAct = (MetaTextArrayAction*) pAction; + Rectangle aRect; + // hdu said base = index + aMapVDev.GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(), + 0, pAct->GetDXArray() ); + Point aPt( pAct->GetPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_STRETCHTEXT_ACTION ): + { + MetaStretchTextAction* pAct = (MetaStretchTextAction*) pAction; + Rectangle aRect; + // hdu said base = index + aMapVDev.GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(), + pAct->GetWidth(), NULL ); + Point aPt( pAct->GetPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXTLINE_ACTION ): + { + MetaTextLineAction* pAct = (MetaTextLineAction*) pAction; + // measure a test string to get ascend and descent right + static const sal_Unicode pStr[] = { 0xc4, 0x67, 0 }; + String aStr( pStr ); + + Rectangle aRect; + aMapVDev.GetTextBoundRect( aRect, aStr, 0, 0, aStr.Len(), 0, NULL ); + Point aPt( pAct->GetStartPoint() ); + aRect.Move( aPt.X(), aPt.Y() ); + aRect.Right() = aRect.Left() + pAct->GetWidth(); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + Rectangle aRect( pAct->GetPoint(), pAct->GetSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + Rectangle aRect( pAct->GetPoint(), pAct->GetSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_GRADIENT_ACTION ): + { + MetaGradientAction* pAct = (MetaGradientAction*) pAction; + Rectangle aRect( pAct->GetRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_GRADIENTEX_ACTION ): + { + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_COMMENT_ACTION ): + { + // nothing to do + }; + break; + + case( META_HATCH_ACTION ): + { + MetaHatchAction* pAct = (MetaHatchAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TRANSPARENT_ACTION ): + { + MetaTransparentAction* pAct = (MetaTransparentAction*) pAction; + Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + MetaFloatTransparentAction* pAct = (MetaFloatTransparentAction*) pAction; + GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() ); + // get the bound rect of the contained metafile + Rectangle aRect( aTransMtf.GetBoundRect( i_rReference ) ); + // scale the rect now on the assumption that the correct top left of the metafile + // (not its bounds !) is (0,0) + Size aPSize( aTransMtf.GetPrefSize() ); + aPSize = aMapVDev.LogicToLogic( aPSize, aTransMtf.GetPrefMapMode(), aMapVDev.GetMapMode() ); + Size aActSize( pAct->GetSize() ); + double fX = double(aActSize.Width())/double(aPSize.Width()); + double fY = double(aActSize.Height())/double(aPSize.Height()); + aRect.Left() = long(double(aRect.Left())*fX); + aRect.Right() = long(double(aRect.Right())*fX); + aRect.Top() = long(double(aRect.Top())*fY); + aRect.Bottom() = long(double(aRect.Bottom())*fY); + + // transform the rect to current VDev state + aRect = aMapVDev.LogicToLogic( aRect, aTransMtf.GetPrefMapMode(), aMapVDev.GetMapMode() ); + + ImplActionBounds( aBound, aRect, aClipStack ); + } + break; + + case( META_EPS_ACTION ): + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + Rectangle aRect( pAct->GetPoint(), pAct->GetSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_CLIPREGION_ACTION ): + { + MetaClipRegionAction* pAct = (MetaClipRegionAction*) pAction; + if( pAct->IsClipping() ) + aClipStack.back() = aMapVDev.LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ); + else + aClipStack.back() = Rectangle(); + } + break; + + case( META_ISECTRECTCLIPREGION_ACTION ): + { + MetaISectRectClipRegionAction* pAct = (MetaISectRectClipRegionAction*) pAction; + Rectangle aRect( aMapVDev.LogicToLogic( pAct->GetRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ) ); + if( aClipStack.back().IsEmpty() ) + aClipStack.back() = aRect; + else + aClipStack.back().Intersection( aRect ); + } + break; + + case( META_ISECTREGIONCLIPREGION_ACTION ): + { + MetaISectRegionClipRegionAction* pAct = (MetaISectRegionClipRegionAction*) pAction; + Rectangle aRect( aMapVDev.LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev.GetMapMode(), GetPrefMapMode() ) ); + if( aClipStack.back().IsEmpty() ) + aClipStack.back() = aRect; + else + aClipStack.back().Intersection( aRect ); + } + break; + + case( META_BMP_ACTION ): + { + MetaBmpAction* pAct = (MetaBmpAction*) pAction; + Rectangle aRect( pAct->GetPoint(), aMapVDev.PixelToLogic( pAct->GetBitmap().GetSizePixel() ) ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_BMPEX_ACTION ): + { + MetaBmpExAction* pAct = (MetaBmpExAction*) pAction; + Rectangle aRect( pAct->GetPoint(), aMapVDev.PixelToLogic( pAct->GetBitmapEx().GetSizePixel() ) ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MASK_ACTION ): + { + MetaMaskAction* pAct = (MetaMaskAction*) pAction; + Rectangle aRect( pAct->GetPoint(), aMapVDev.PixelToLogic( pAct->GetBitmap().GetSizePixel() ) ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MASKSCALE_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MASKSCALEPART_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_WALLPAPER_ACTION ): + { + MetaWallpaperAction* pAct = (MetaWallpaperAction*) pAction; + Rectangle aRect( pAct->GetRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_TEXTRECT_ACTION ): + { + MetaTextRectAction* pAct = (MetaTextRectAction*) pAction; + Rectangle aRect( pAct->GetRect() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + case( META_MOVECLIPREGION_ACTION ): + { + MetaMoveClipRegionAction* pAct = (MetaMoveClipRegionAction*) pAction; + if( ! aClipStack.back().IsEmpty() ) + { + Size aDelta( pAct->GetHorzMove(), pAct->GetVertMove() ); + aDelta = aMapVDev.LogicToLogic( aDelta, aMapVDev.GetMapMode(), GetPrefMapMode() ); + aClipStack.back().Move( aDelta.Width(), aDelta.Width() ); + } + } + break; + + case( META_RENDERGRAPHIC_ACTION ): + { + MetaRenderGraphicAction* pAct = (MetaRenderGraphicAction*) pAction; + Rectangle aRect( pAct->GetPoint(), pAct->GetSize() ); + ImplActionBounds( aBound, aMapVDev.LogicToLogic( aRect, aMapVDev.GetMapMode(), GetPrefMapMode() ), aClipStack ); + } + break; + + default: + { + pAction->Execute( &aMapVDev ); + + if( nActionType == META_PUSH_ACTION ) + { + MetaPushAction* pAct = (MetaPushAction*) pAction; + aPushFlagStack.push_back( pAct->GetFlags() ); + if( (aPushFlagStack.back() & PUSH_CLIPREGION) != 0 ) + { + Rectangle aRect( aClipStack.back() ); + aClipStack.push_back( aRect ); + } + } + else if( nActionType == META_POP_ACTION ) + { + // sanity check + if( ! aPushFlagStack.empty() ) + { + if( (aPushFlagStack.back() & PUSH_CLIPREGION) != 0 ) + { + if( aClipStack.size() > 1 ) + aClipStack.pop_back(); + } + aPushFlagStack.pop_back(); + } + } + } + break; + } + } + return aBound; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColAdjustFnc( const Color& rColor, const void* pColParam ) +{ + return Color( rColor.GetTransparency(), + ( (const ImplColAdjustParam*) pColParam )->pMapR[ rColor.GetRed() ], + ( (const ImplColAdjustParam*) pColParam )->pMapG[ rColor.GetGreen() ], + ( (const ImplColAdjustParam*) pColParam )->pMapB[ rColor.GetBlue() ] ); + +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpAdjustFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + const ImplBmpAdjustParam* p = (const ImplBmpAdjustParam*) pBmpParam; + BitmapEx aRet( rBmpEx ); + + aRet.Adjust( p->nLuminancePercent, p->nContrastPercent, + p->nChannelRPercent, p->nChannelGPercent, p->nChannelBPercent, + p->fGamma, p->bInvert ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColConvertFnc( const Color& rColor, const void* pColParam ) +{ + sal_uInt8 cLum = rColor.GetLuminance(); + + if( MTF_CONVERSION_1BIT_THRESHOLD == ( (const ImplColConvertParam*) pColParam )->eConversion ) + cLum = ( cLum < 128 ) ? 0 : 255; + + return Color( rColor.GetTransparency(), cLum, cLum, cLum ); +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpConvertFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + BitmapEx aRet( rBmpEx ); + + aRet.Convert( ( (const ImplBmpConvertParam*) pBmpParam )->eConversion ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColMonoFnc( const Color&, const void* pColParam ) +{ + return( ( (const ImplColMonoParam*) pColParam )->aColor ); +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpMonoFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + BitmapPalette aPal( 3 ); + + aPal[ 0 ] = Color( COL_BLACK ); + aPal[ 1 ] = Color( COL_WHITE ); + aPal[ 2 ] = ( (const ImplBmpMonoParam*) pBmpParam )->aColor; + + Bitmap aBmp( rBmpEx.GetSizePixel(), 4, &aPal ); + aBmp.Erase( ( (const ImplBmpMonoParam*) pBmpParam )->aColor ); + + if( rBmpEx.IsAlpha() ) + return BitmapEx( aBmp, rBmpEx.GetAlpha() ); + else if( rBmpEx.IsTransparent() ) + return BitmapEx( aBmp, rBmpEx.GetMask() ); + else + return aBmp; +} + +// ------------------------------------------------------------------------ + +Color GDIMetaFile::ImplColReplaceFnc( const Color& rColor, const void* pColParam ) +{ + const sal_uLong nR = rColor.GetRed(), nG = rColor.GetGreen(), nB = rColor.GetBlue(); + + for( sal_uLong i = 0; i < ( (const ImplColReplaceParam*) pColParam )->nCount; i++ ) + { + if( ( ( (const ImplColReplaceParam*) pColParam )->pMinR[ i ] <= nR ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMaxR[ i ] >= nR ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMinG[ i ] <= nG ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMaxG[ i ] >= nG ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMinB[ i ] <= nB ) && + ( ( (const ImplColReplaceParam*) pColParam )->pMaxB[ i ] >= nB ) ) + { + return( ( (const ImplColReplaceParam*) pColParam )->pDstCols[ i ] ); + } + } + + return rColor; +} + +// ------------------------------------------------------------------------ + +BitmapEx GDIMetaFile::ImplBmpReplaceFnc( const BitmapEx& rBmpEx, const void* pBmpParam ) +{ + const ImplBmpReplaceParam* p = (const ImplBmpReplaceParam*) pBmpParam; + BitmapEx aRet( rBmpEx ); + + aRet.Replace( p->pSrcCols, p->pDstCols, p->nCount, p->pTols ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pColParam, + BmpExchangeFnc pFncBmp, const void* pBmpParam ) +{ + GDIMetaFile aMtf; + + aMtf.aPrefSize = aPrefSize; + aMtf.aPrefMapMode = aPrefMapMode; + + for( MetaAction* pAction = (MetaAction*) First(); pAction; pAction = (MetaAction*) Next() ) + { + const sal_uInt16 nType = pAction->GetType(); + + switch( nType ) + { + case( META_PIXEL_ACTION ): + { + MetaPixelAction* pAct = (MetaPixelAction*) pAction; + aMtf.Insert( new MetaPixelAction( pAct->GetPoint(), pFncCol( pAct->GetColor(), pColParam ) ), LIST_APPEND ); + } + break; + + case( META_LINECOLOR_ACTION ): + { + MetaLineColorAction* pAct = (MetaLineColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaLineColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_FILLCOLOR_ACTION ): + { + MetaFillColorAction* pAct = (MetaFillColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaFillColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_TEXTCOLOR_ACTION ): + { + MetaTextColorAction* pAct = (MetaTextColorAction*) pAction; + aMtf.Insert( new MetaTextColorAction( pFncCol( pAct->GetColor(), pColParam ) ), LIST_APPEND ); + } + break; + + case( META_TEXTFILLCOLOR_ACTION ): + { + MetaTextFillColorAction* pAct = (MetaTextFillColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaTextFillColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_TEXTLINECOLOR_ACTION ): + { + MetaTextLineColorAction* pAct = (MetaTextLineColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaTextLineColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_OVERLINECOLOR_ACTION ): + { + MetaOverlineColorAction* pAct = (MetaOverlineColorAction*) pAction; + + if( !pAct->IsSetting() ) + pAct->Duplicate(); + else + pAct = new MetaOverlineColorAction( pFncCol( pAct->GetColor(), pColParam ), sal_True ); + + aMtf.Insert( pAct, LIST_APPEND ); + } + break; + + case( META_FONT_ACTION ): + { + MetaFontAction* pAct = (MetaFontAction*) pAction; + Font aFont( pAct->GetFont() ); + + aFont.SetColor( pFncCol( aFont.GetColor(), pColParam ) ); + aFont.SetFillColor( pFncCol( aFont.GetFillColor(), pColParam ) ); + aMtf.Insert( new MetaFontAction( aFont ), LIST_APPEND ); + } + break; + + case( META_WALLPAPER_ACTION ): + { + MetaWallpaperAction* pAct = (MetaWallpaperAction*) pAction; + Wallpaper aWall( pAct->GetWallpaper() ); + const Rectangle& rRect = pAct->GetRect(); + + aWall.SetColor( pFncCol( aWall.GetColor(), pColParam ) ); + + if( aWall.IsBitmap() ) + aWall.SetBitmap( pFncBmp( aWall.GetBitmap(), pBmpParam ) ); + + if( aWall.IsGradient() ) + { + Gradient aGradient( aWall.GetGradient() ); + + aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) ); + aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) ); + aWall.SetGradient( aGradient ); + } + + aMtf.Insert( new MetaWallpaperAction( rRect, aWall ), LIST_APPEND ); + } + break; + + case( META_BMP_ACTION ): + case( META_BMPEX_ACTION ): + case( META_MASK_ACTION ): + { + DBG_ERROR( "Don't use bitmap actions of this type in metafiles!" ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + aMtf.Insert( new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(), + pFncBmp( pAct->GetBitmap(), pBmpParam ).GetBitmap() ), + LIST_APPEND ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + aMtf.Insert( new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), + pFncBmp( pAct->GetBitmap(), pBmpParam ).GetBitmap() ), + LIST_APPEND ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + aMtf.Insert( new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(), + pFncBmp( pAct->GetBitmapEx(), pBmpParam ) ), + LIST_APPEND ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + aMtf.Insert( new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), + pFncBmp( pAct->GetBitmapEx(), pBmpParam ) ), + LIST_APPEND ); + } + break; + + case( META_MASKSCALE_ACTION ): + { + MetaMaskScaleAction* pAct = (MetaMaskScaleAction*) pAction; + aMtf.Insert( new MetaMaskScaleAction( pAct->GetPoint(), pAct->GetSize(), + pAct->GetBitmap(), + pFncCol( pAct->GetColor(), pColParam ) ), + LIST_APPEND ); + } + break; + + case( META_MASKSCALEPART_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + aMtf.Insert( new MetaMaskScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), + pAct->GetBitmap(), + pFncCol( pAct->GetColor(), pColParam ) ), + LIST_APPEND ); + } + break; + + case( META_GRADIENT_ACTION ): + { + MetaGradientAction* pAct = (MetaGradientAction*) pAction; + Gradient aGradient( pAct->GetGradient() ); + + aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) ); + aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) ); + aMtf.Insert( new MetaGradientAction( pAct->GetRect(), aGradient ), LIST_APPEND ); + } + break; + + case( META_GRADIENTEX_ACTION ): + { + MetaGradientExAction* pAct = (MetaGradientExAction*) pAction; + Gradient aGradient( pAct->GetGradient() ); + + aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) ); + aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) ); + aMtf.Insert( new MetaGradientExAction( pAct->GetPolyPolygon(), aGradient ), LIST_APPEND ); + } + break; + + case( META_HATCH_ACTION ): + { + MetaHatchAction* pAct = (MetaHatchAction*) pAction; + Hatch aHatch( pAct->GetHatch() ); + + aHatch.SetColor( pFncCol( aHatch.GetColor(), pColParam ) ); + aMtf.Insert( new MetaHatchAction( pAct->GetPolyPolygon(), aHatch ), LIST_APPEND ); + } + break; + + case( META_FLOATTRANSPARENT_ACTION ): + { + MetaFloatTransparentAction* pAct = (MetaFloatTransparentAction*) pAction; + GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() ); + + aTransMtf.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam ); + aMtf.Insert( new MetaFloatTransparentAction( aTransMtf, + pAct->GetPoint(), pAct->GetSize(), + pAct->GetGradient() ), + LIST_APPEND ); + } + break; + + case( META_EPS_ACTION ): + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + GDIMetaFile aSubst( pAct->GetSubstitute() ); + + aSubst.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam ); + aMtf.Insert( new MetaEPSAction( pAct->GetPoint(), pAct->GetSize(), + pAct->GetLink(), aSubst ), + LIST_APPEND ); + } + break; + + case( META_RENDERGRAPHIC_ACTION ): + { + OSL_TRACE( "ExchangeColors not supported for RenderGraphic MetaActions yet" ); + + pAction->Duplicate(); + aMtf.Insert( pAction, LIST_APPEND ); + } + break; + + default: + { + pAction->Duplicate(); + aMtf.Insert( pAction, LIST_APPEND ); + } + break; + } + } + + *this = aMtf; +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent, + short nChannelRPercent, short nChannelGPercent, + short nChannelBPercent, double fGamma, sal_Bool bInvert ) +{ + // nothing to do? => return quickly + if( nLuminancePercent || nContrastPercent || + nChannelRPercent || nChannelGPercent || nChannelBPercent || + ( fGamma != 1.0 ) || bInvert ) + { + double fM, fROff, fGOff, fBOff, fOff; + ImplColAdjustParam aColParam; + ImplBmpAdjustParam aBmpParam; + + aColParam.pMapR = new sal_uInt8[ 256 ]; + aColParam.pMapG = new sal_uInt8[ 256 ]; + aColParam.pMapB = new sal_uInt8[ 256 ]; + + // calculate slope + if( nContrastPercent >= 0 ) + fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0L, 100L ) ); + else + fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100L, 0L ) ) / 128.0; + + // total offset = luminance offset + contrast offset + fOff = MinMax( nLuminancePercent, -100L, 100L ) * 2.55 + 128.0 - fM * 128.0; + + // channel offset = channel offset + total offset + fROff = nChannelRPercent * 2.55 + fOff; + fGOff = nChannelGPercent * 2.55 + fOff; + fBOff = nChannelBPercent * 2.55 + fOff; + + // calculate gamma value + fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma ); + const sal_Bool bGamma = ( fGamma != 1.0 ); + + // create mapping table + for( long nX = 0L; nX < 256L; nX++ ) + { + aColParam.pMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0L, 255L ); + aColParam.pMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0L, 255L ); + aColParam.pMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0L, 255L ); + + if( bGamma ) + { + aColParam.pMapR[ nX ] = GAMMA( aColParam.pMapR[ nX ], fGamma ); + aColParam.pMapG[ nX ] = GAMMA( aColParam.pMapG[ nX ], fGamma ); + aColParam.pMapB[ nX ] = GAMMA( aColParam.pMapB[ nX ], fGamma ); + } + + if( bInvert ) + { + aColParam.pMapR[ nX ] = ~aColParam.pMapR[ nX ]; + aColParam.pMapG[ nX ] = ~aColParam.pMapG[ nX ]; + aColParam.pMapB[ nX ] = ~aColParam.pMapB[ nX ]; + } + } + + aBmpParam.nLuminancePercent = nLuminancePercent; + aBmpParam.nContrastPercent = nContrastPercent; + aBmpParam.nChannelRPercent = nChannelRPercent; + aBmpParam.nChannelGPercent = nChannelGPercent; + aBmpParam.nChannelBPercent = nChannelBPercent; + aBmpParam.fGamma = fGamma; + aBmpParam.bInvert = bInvert; + + // do color adjustment + ImplExchangeColors( ImplColAdjustFnc, &aColParam, ImplBmpAdjustFnc, &aBmpParam ); + + delete[] aColParam.pMapR; + delete[] aColParam.pMapG; + delete[] aColParam.pMapB; + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::Convert( MtfConversion eConversion ) +{ + // nothing to do? => return quickly + if( eConversion != MTF_CONVERSION_NONE ) + { + ImplColConvertParam aColParam; + ImplBmpConvertParam aBmpParam; + + aColParam.eConversion = eConversion; + aBmpParam.eConversion = ( MTF_CONVERSION_1BIT_THRESHOLD == eConversion ) ? BMP_CONVERSION_1BIT_THRESHOLD : BMP_CONVERSION_8BIT_GREYS; + + ImplExchangeColors( ImplColConvertFnc, &aColParam, ImplBmpConvertFnc, &aBmpParam ); + } +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ReplaceColors( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol ) +{ + ReplaceColors( &rSearchColor, &rReplaceColor, 1, &nTol ); +} + +// ------------------------------------------------------------------------ + +void GDIMetaFile::ReplaceColors( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount, sal_uLong* pTols ) +{ + ImplColReplaceParam aColParam; + ImplBmpReplaceParam aBmpParam; + + aColParam.pMinR = new sal_uLong[ nColorCount ]; + aColParam.pMaxR = new sal_uLong[ nColorCount ]; + aColParam.pMinG = new sal_uLong[ nColorCount ]; + aColParam.pMaxG = new sal_uLong[ nColorCount ]; + aColParam.pMinB = new sal_uLong[ nColorCount ]; + aColParam.pMaxB = new sal_uLong[ nColorCount ]; + + for( sal_uLong i = 0; i < nColorCount; i++ ) + { + const long nTol = pTols ? ( pTols[ i ] * 255 ) / 100 : 0; + long nVal; + + nVal = pSearchColors[ i ].GetRed(); + aColParam.pMinR[ i ] = (sal_uLong) Max( nVal - nTol, 0L ); + aColParam.pMaxR[ i ] = (sal_uLong) Min( nVal + nTol, 255L ); + + nVal = pSearchColors[ i ].GetGreen(); + aColParam.pMinG[ i ] = (sal_uLong) Max( nVal - nTol, 0L ); + aColParam.pMaxG[ i ] = (sal_uLong) Min( nVal + nTol, 255L ); + + nVal = pSearchColors[ i ].GetBlue(); + aColParam.pMinB[ i ] = (sal_uLong) Max( nVal - nTol, 0L ); + aColParam.pMaxB[ i ] = (sal_uLong) Min( nVal + nTol, 255L ); + } + + aColParam.pDstCols = pReplaceColors; + aColParam.nCount = nColorCount; + + aBmpParam.pSrcCols = pSearchColors; + aBmpParam.pDstCols = pReplaceColors; + aBmpParam.nCount = nColorCount; + aBmpParam.pTols = pTols; + + ImplExchangeColors( ImplColReplaceFnc, &aColParam, ImplBmpReplaceFnc, &aBmpParam ); + + delete[] aColParam.pMinR; + delete[] aColParam.pMaxR; + delete[] aColParam.pMinG; + delete[] aColParam.pMaxG; + delete[] aColParam.pMinB; + delete[] aColParam.pMaxB; +}; + +// ------------------------------------------------------------------------ + +GDIMetaFile GDIMetaFile::GetMonochromeMtf( const Color& rColor ) const +{ + GDIMetaFile aRet( *this ); + + ImplColMonoParam aColParam; + ImplBmpMonoParam aBmpParam; + + aColParam.aColor = rColor; + aBmpParam.aColor = rColor; + + aRet.ImplExchangeColors( ImplColMonoFnc, &aColParam, ImplBmpMonoFnc, &aBmpParam ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetChecksum() const +{ + GDIMetaFile aMtf; + SvMemoryStream aMemStm( 65535, 65535 ); + ImplMetaWriteData aWriteData; + SVBT16 aBT16; + SVBT32 aBT32; + sal_uLong nCrc = 0; + + aWriteData.meActualCharSet = aMemStm.GetStreamCharSet(); + + for( sal_uLong i = 0, nObjCount = GetActionCount(); i < nObjCount; i++ ) + { + MetaAction* pAction = GetAction( i ); + + switch( pAction->GetType() ) + { + case( META_BMP_ACTION ): + { + MetaBmpAction* pAct = (MetaBmpAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPSCALE_ACTION ): + { + MetaBmpScaleAction* pAct = (MetaBmpScaleAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPSCALEPART_ACTION ): + { + MetaBmpScalePartAction* pAct = (MetaBmpScalePartAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPEX_ACTION ): + { + MetaBmpExAction* pAct = (MetaBmpExAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmapEx().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPEXSCALE_ACTION ): + { + MetaBmpExScaleAction* pAct = (MetaBmpExScaleAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmapEx().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_BMPEXSCALEPART_ACTION ): + { + MetaBmpExScalePartAction* pAct = (MetaBmpExScalePartAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmapEx().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_MASK_ACTION ): + { + MetaMaskAction* pAct = (MetaMaskAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_MASKSCALE_ACTION ): + { + MetaMaskScaleAction* pAct = (MetaMaskScaleAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case( META_MASKSCALEPART_ACTION ): + { + MetaMaskScalePartAction* pAct = (MetaMaskScalePartAction*) pAction; + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + UInt32ToSVBT32( pAct->GetBitmap().GetChecksum(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + case META_EPS_ACTION : + { + MetaEPSAction* pAct = (MetaEPSAction*) pAction; + nCrc = rtl_crc32( nCrc, pAct->GetLink().GetData(), pAct->GetLink().GetDataSize() ); + } + break; + + case( META_RENDERGRAPHIC_ACTION ): + { + MetaRenderGraphicAction* pAct = (MetaRenderGraphicAction*) pAction; + const ::vcl::RenderGraphic& rRenderGraphic = pAct->GetRenderGraphic(); + + ShortToSVBT16( pAct->GetType(), aBT16 ); + nCrc = rtl_crc32( nCrc, aBT16, 2 ); + + nCrc = rtl_crc32( nCrc, rRenderGraphic.GetGraphicData().get(), rRenderGraphic.GetGraphicDataLength() ); + + UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + + UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 ); + nCrc = rtl_crc32( nCrc, aBT32, 4 ); + } + break; + + default: + { + pAction->Write( aMemStm, &aWriteData ); + nCrc = rtl_crc32( nCrc, aMemStm.GetData(), aMemStm.Tell() ); + aMemStm.Seek( 0 ); + } + break; + } + } + + return nCrc; +} + +// ------------------------------------------------------------------------ + +sal_uLong GDIMetaFile::GetSizeBytes() const +{ + sal_uLong nSizeBytes = 0; + + for( sal_uLong i = 0, nObjCount = GetActionCount(); i < nObjCount; ++i ) + { + MetaAction* pAction = GetAction( i ); + + // default action size is set to 32 (=> not the exact value) + nSizeBytes += 32; + + // add sizes for large action content + switch( pAction->GetType() ) + { + case( META_BMP_ACTION ): nSizeBytes += ( (MetaBmpAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_BMPSCALE_ACTION ): nSizeBytes += ( (MetaBmpScaleAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_BMPSCALEPART_ACTION ): nSizeBytes += ( (MetaBmpScalePartAction*) pAction )->GetBitmap().GetSizeBytes(); break; + + case( META_BMPEX_ACTION ): nSizeBytes += ( (MetaBmpExAction*) pAction )->GetBitmapEx().GetSizeBytes(); break; + case( META_BMPEXSCALE_ACTION ): nSizeBytes += ( (MetaBmpExScaleAction*) pAction )->GetBitmapEx().GetSizeBytes(); break; + case( META_BMPEXSCALEPART_ACTION ): nSizeBytes += ( (MetaBmpExScalePartAction*) pAction )->GetBitmapEx().GetSizeBytes(); break; + + case( META_MASK_ACTION ): nSizeBytes += ( (MetaMaskAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_MASKSCALE_ACTION ): nSizeBytes += ( (MetaMaskScaleAction*) pAction )->GetBitmap().GetSizeBytes(); break; + case( META_MASKSCALEPART_ACTION ): nSizeBytes += ( (MetaMaskScalePartAction*) pAction )->GetBitmap().GetSizeBytes(); break; + + case( META_POLYLINE_ACTION ): nSizeBytes += ( ( (MetaPolyLineAction*) pAction )->GetPolygon().GetSize() * sizeof( Point ) ); break; + case( META_POLYGON_ACTION ): nSizeBytes += ( ( (MetaPolygonAction*) pAction )->GetPolygon().GetSize() * sizeof( Point ) ); break; + case( META_POLYPOLYGON_ACTION ): + { + const PolyPolygon& rPolyPoly = ( (MetaPolyPolygonAction*) pAction )->GetPolyPolygon(); + + for( sal_uInt16 n = 0; n < rPolyPoly.Count(); ++n ) + nSizeBytes += ( rPolyPoly[ n ].GetSize() * sizeof( Point ) ); + } + break; + + case( META_TEXT_ACTION ): nSizeBytes += ( ( (MetaTextAction*) pAction )->GetText().Len() * sizeof( sal_Unicode ) ); break; + case( META_STRETCHTEXT_ACTION ): nSizeBytes += ( ( (MetaStretchTextAction*) pAction )->GetText().Len() * sizeof( sal_Unicode ) ); break; + case( META_TEXTRECT_ACTION ): nSizeBytes += ( ( (MetaTextRectAction*) pAction )->GetText().Len() * sizeof( sal_Unicode ) ); break; + case( META_TEXTARRAY_ACTION ): + { + MetaTextArrayAction* pTextArrayAction = (MetaTextArrayAction*) pAction; + + nSizeBytes += ( pTextArrayAction->GetText().Len() * sizeof( sal_Unicode ) ); + + if( pTextArrayAction->GetDXArray() ) + nSizeBytes += ( pTextArrayAction->GetLen() << 2 ); + } + break; + + case( META_RENDERGRAPHIC_ACTION ): nSizeBytes += ( ( (MetaRenderGraphicAction*) pAction )->GetRenderGraphic() ).GetGraphicDataLength(); break; + } + } + + return( nSizeBytes ); +} + +// ------------------------------------------------------------------------ + +SvStream& operator>>( SvStream& rIStm, GDIMetaFile& rGDIMetaFile ) +{ + if( !rIStm.GetError() ) + { + char aId[ 7 ]; + sal_uLong nStmPos = rIStm.Tell(); + sal_uInt16 nOldFormat = rIStm.GetNumberFormatInt(); + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + aId[ 0 ] = 0; + aId[ 6 ] = 0; + rIStm.Read( aId, 6 ); + + if ( !strcmp( aId, "VCLMTF" ) ) + { + // new format + VersionCompat* pCompat; + MetaAction* pAction; + sal_uInt32 nStmCompressMode = 0; + sal_uInt32 nCount = 0; + sal_uInt8 bRenderGraphicReplacements = 0; + + pCompat = new VersionCompat( rIStm, STREAM_READ ); + { + // version 1 + rIStm >> nStmCompressMode; + rIStm >> rGDIMetaFile.aPrefMapMode; + rIStm >> rGDIMetaFile.aPrefSize; + rIStm >> nCount; + + if( pCompat->GetVersion() >= 2 ) + { + // version 2 + // ========= + // contains an additional flag to indicate that RenderGraphic + // actions are immediately followed by a replacement image, that + // needs to be skipped in case the flag is set (KA 01/2011) + + rIStm >> bRenderGraphicReplacements; + } + } + delete pCompat; + + ImplMetaReadData aReadData; + aReadData.meActualCharSet = rIStm.GetStreamCharSet(); + + for( sal_uInt32 nAction = 0UL; ( nAction < nCount ) && !rIStm.IsEof(); ++nAction ) + { + pAction = MetaAction::ReadMetaAction( rIStm, &aReadData ); + + if( pAction ) + { + rGDIMetaFile.AddAction( pAction ); + + // if the MetaFile was written in RenderGraphics replacement mode + // and we just read a RenderGraphic action, skip the following + // META_BMPEXSCALE_ACTION, since this is the replacement image, + // just needed for old implementations; don't forget to increment + // the action read counter! (KA 01/2011) + if( bRenderGraphicReplacements && + ( META_RENDERGRAPHIC_ACTION == pAction->GetType() ) && + ( ++nAction < nCount ) && !rIStm.IsEof() ) + { + sal_uInt16 nFollowingType; + + // dummy read of the next following META_BMPEXSCALE_ACTION + // RenderGraphic replacement action (KA 01/2011) + rIStm >> nFollowingType; + delete ( new VersionCompat( rIStm, STREAM_READ ) ); + + OSL_ENSURE( META_BMPEXSCALE_ACTION == nFollowingType, \ +"META_RENDERGRAPHIC_ACTION read in RenderGraphic replacement mode \ +without following META_BMPEXSCALE_ACTION replacement" ); + } + } + } + } + else + { + // to avoid possible compiler optimizations => new/delete + rIStm.Seek( nStmPos ); + delete( new SVMConverter( rIStm, rGDIMetaFile, CONVERT_FROM_SVM1 ) ); + } + + // check for errors + if( rIStm.GetError() ) + { + rGDIMetaFile.Clear(); + rIStm.Seek( nStmPos ); + } + + rIStm.SetNumberFormatInt( nOldFormat ); + } + + return rIStm; +} + +// ------------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStm, const GDIMetaFile& rGDIMetaFile ) +{ + if( !rOStm.GetError() ) + { + static const char* pEnableSVM1 = getenv( "SAL_ENABLE_SVM1" ); + static const bool bNoSVM1 = (NULL == pEnableSVM1 ) || ( '0' == *pEnableSVM1 ); + + if( bNoSVM1 || rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) + { + const_cast< GDIMetaFile& >( rGDIMetaFile ).Write( rOStm ); + } + else + { + delete( new SVMConverter( rOStm, const_cast< GDIMetaFile& >( rGDIMetaFile ), CONVERT_TO_SVM1 ) ); + } + +#ifdef DEBUG + if( !bNoSVM1 && rOStm.GetVersion() < SOFFICE_FILEFORMAT_50 ) + { +OSL_TRACE( \ +"GDIMetaFile would normally be written in old SVM1 format by this call. \ +The current implementation always writes in VCLMTF format. \ +Please set environment variable SAL_ENABLE_SVM1 to '1' to reenable old behavior" ); + } +#endif // DEBUG + } + + return rOStm; +} + +// ------------------------------------------------------------------------ + +SvStream& GDIMetaFile::Read( SvStream& rIStm ) +{ + Clear(); + rIStm >> *this; + + return rIStm; +} + +// ------------------------------------------------------------------------ + +SvStream& GDIMetaFile::Write( SvStream& rOStm, GDIMetaFileWriteFlags nWriteFlags ) +{ + VersionCompat* pCompat; + const sal_uInt32 nStmCompressMode = rOStm.GetCompressMode(); + sal_uInt16 nOldFormat = rOStm.GetNumberFormatInt(); + const sal_uInt8 bRenderGraphicReplacements = + ( ( ( GDIMETAFILE_WRITE_REPLACEMENT_RENDERGRAPHIC & nWriteFlags ) != 0 ) ? 1 : 0 ); + + // With the introduction of the META_RENDERGRAPHIC_ACTION, it is neccessary + // to provide some kind of document backward compatibility: + // + // If the flag GDIMETAFILE_WRITE_REPLACEMENT_RENDERGRAPHIC is set in + // parameter nWriteFlags, each META_RENDERGRAPHIC_ACTION is followed by + // an additional META_BMPEXSCALE_ACTION, that contains a replacement + // image for the new RenderGraphic action. + // + // Old implementations, not knowing anything about META_RENDERGRAPHIC_ACTION, + // will skip this new action and read the META_BMPEXSCALE_ACTION instead + // + // Since the current implementation is able to handle the new action, the + // then following image replacement action needs to be skipped by this + // implementation, if the metafile was written in the RenderGraphic + // replacement mode. + // + // To be able to detect this compatibility mode, the header needs to + // be extended by a corresponding flag, resulting in version 2 of + // the header. The surrounding VersionCompat of the header + // allows to add such new data without any problems (KA 01/2011) + + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + rOStm.Write( "VCLMTF", 6 ); + + pCompat = new VersionCompat( rOStm, STREAM_WRITE, 2 ); + + { + // version 1 + sal_uInt32 nActionCount = 0; + + // calculate correct action count and watch for + // additional RenderGraphic replacement actions, if the + // GDIMETAFILE_WRITE_REPLACEMENT_RENDERGRAPHIC is set + // and META_RENDERGRAPHIC_ACTION are encountered (KA 01/2011) + for( MetaAction* pAct = static_cast< MetaAction* >( First() ); pAct; pAct = static_cast< MetaAction* >( Next() ) ) + { + nActionCount += ( bRenderGraphicReplacements && ( META_RENDERGRAPHIC_ACTION == pAct->GetType() ) ? 2 : 1 ); + } + + rOStm << nStmCompressMode << aPrefMapMode << aPrefSize << nActionCount; + + { + // version 2 + // ========= + // since version 2, a GDIMETAFILE_WRITE_REPLACEMENT_RENDERGRAPHIC flag + // is written, to indicate that each META_BMPEXSCALE_ACTION following + // a META_RENDERGRAPHIC_ACTION needs to be skipped, in case the flag is + // set (KA 01/2011) + rOStm << bRenderGraphicReplacements; + } + } + + delete pCompat; + + ImplMetaWriteData aWriteData; + + aWriteData.meActualCharSet = rOStm.GetStreamCharSet(); + aWriteData.mnWriteFlags = nWriteFlags; + + for( MetaAction* pAct = static_cast< MetaAction* >( First() ); pAct; pAct = static_cast< MetaAction* >( Next() ) ) + { + pAct->Write( rOStm, &aWriteData ); + + // write the RenderGraphic replacement image, if the + // GDIMETAFILE_WRITE_REPLACEMENT_RENDERGRAPHIC flag is set + // and if a META_RENDERGRAPHIC_ACTION is encountered (KA 01/2011) + if( bRenderGraphicReplacements && ( META_RENDERGRAPHIC_ACTION == pAct->GetType() ) ) + { + MetaRenderGraphicAction* pRenderAction = static_cast< MetaRenderGraphicAction* >( pAct ); + MetaBmpExScaleAction* pBmpExScaleAction = new MetaBmpExScaleAction( + pRenderAction->GetPoint(), pRenderAction->GetSize(), + pRenderAction->GetRenderGraphic().GetReplacement() ); + + pBmpExScaleAction->Write( rOStm, &aWriteData ); + pBmpExScaleAction->Delete(); + } + } + + rOStm.SetNumberFormatInt( nOldFormat ); + + return rOStm; +} + +// ------------------------------------------------------------------------ + +sal_Bool GDIMetaFile::CreateThumbnail( sal_uInt32 nMaximumExtent, + BitmapEx& rBmpEx, + const BitmapEx* pOverlay, + const Rectangle* pOverlayRect ) const +{ + // the implementation is provided by KA + + // initialization seems to be complicated but is used to avoid rounding errors + VirtualDevice aVDev; + const Point aNullPt; + const Point aTLPix( aVDev.LogicToPixel( aNullPt, GetPrefMapMode() ) ); + const Point aBRPix( aVDev.LogicToPixel( Point( GetPrefSize().Width() - 1, GetPrefSize().Height() - 1 ), GetPrefMapMode() ) ); + Size aDrawSize( aVDev.LogicToPixel( GetPrefSize(), GetPrefMapMode() ) ); + Size aSizePix( labs( aBRPix.X() - aTLPix.X() ) + 1, labs( aBRPix.Y() - aTLPix.Y() ) + 1 ); + Point aPosPix; + + if ( !rBmpEx.IsEmpty() ) + rBmpEx.SetEmpty(); + + // determine size that has the same aspect ratio as image size and + // fits into the rectangle determined by nMaximumExtent + if ( aSizePix.Width() && aSizePix.Height() + && ( sal::static_int_cast< unsigned long >(aSizePix.Width()) > + nMaximumExtent || + sal::static_int_cast< unsigned long >(aSizePix.Height()) > + nMaximumExtent ) ) + { + const Size aOldSizePix( aSizePix ); + double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height(); + + if ( fWH <= 1.0 ) + { + aSizePix.Width() = FRound( nMaximumExtent * fWH ); + aSizePix.Height() = nMaximumExtent; + } + else + { + aSizePix.Width() = nMaximumExtent; + aSizePix.Height() = FRound( nMaximumExtent / fWH ); + } + + aDrawSize.Width() = FRound( ( static_cast< double >( aDrawSize.Width() ) * aSizePix.Width() ) / aOldSizePix.Width() ); + aDrawSize.Height() = FRound( ( static_cast< double >( aDrawSize.Height() ) * aSizePix.Height() ) / aOldSizePix.Height() ); + } + + Size aFullSize; + Point aBackPosPix; + Rectangle aOverlayRect; + + // calculate addigtional positions and sizes if an overlay image is used + if ( pOverlay ) + { + aFullSize = Size( nMaximumExtent, nMaximumExtent ); + aOverlayRect = Rectangle( aNullPt, aFullSize ); + + aOverlayRect.Intersection( pOverlayRect ? *pOverlayRect : Rectangle( aNullPt, pOverlay->GetSizePixel() ) ); + + if ( !aOverlayRect.IsEmpty() ) + aBackPosPix = Point( ( nMaximumExtent - aSizePix.Width() ) >> 1, ( nMaximumExtent - aSizePix.Height() ) >> 1 ); + else + pOverlay = NULL; + } + else + { + aFullSize = aSizePix; + pOverlay = NULL; + } + + // draw image(s) into VDev and get resulting image + if ( aVDev.SetOutputSizePixel( aFullSize ) ) + { + // draw metafile into VDev + const_cast<GDIMetaFile *>(this)->WindStart(); + const_cast<GDIMetaFile *>(this)->Play( &aVDev, aBackPosPix, aDrawSize ); + + // draw overlay if neccessary + if ( pOverlay ) + aVDev.DrawBitmapEx( aOverlayRect.TopLeft(), aOverlayRect.GetSize(), *pOverlay ); + + // get paint bitmap + Bitmap aBmp( aVDev.GetBitmap( aNullPt, aVDev.GetOutputSizePixel() ) ); + + // assure that we have a true color image + if ( aBmp.GetBitCount() != 24 ) + aBmp.Convert( BMP_CONVERSION_24BIT ); + + // create resulting mask bitmap with metafile output set to black + GDIMetaFile aMonchromeMtf( GetMonochromeMtf( COL_BLACK ) ); + aVDev.DrawWallpaper( Rectangle( aNullPt, aSizePix ), Wallpaper( Color( COL_WHITE ) ) ); + aMonchromeMtf.WindStart(); + aMonchromeMtf.Play( &aVDev, aBackPosPix, aDrawSize ); + + // watch for overlay mask + if ( pOverlay ) + { + Bitmap aOverlayMergeBmp( aVDev.GetBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize() ) ); + + // create ANDed resulting mask at overlay area + if ( pOverlay->IsTransparent() ) + aVDev.DrawBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize(), pOverlay->GetMask() ); + else + { + aVDev.SetLineColor( COL_BLACK ); + aVDev.SetFillColor( COL_BLACK ); + aVDev.DrawRect( aOverlayRect); + } + + aOverlayMergeBmp.CombineSimple( aVDev.GetBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize() ), BMP_COMBINE_AND ); + aVDev.DrawBitmap( aOverlayRect.TopLeft(), aOverlayRect.GetSize(), aOverlayMergeBmp ); + } + + rBmpEx = BitmapEx( aBmp, aVDev.GetBitmap( aNullPt, aVDev.GetOutputSizePixel() ) ); + } + + return !rBmpEx.IsEmpty(); +} |