summaryrefslogtreecommitdiffstats
path: root/vcl/source/gdi/gdimtf.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/gdi/gdimtf.cxx')
-rw-r--r--vcl/source/gdi/gdimtf.cxx2357
1 files changed, 2357 insertions, 0 deletions
diff --git a/vcl/source/gdi/gdimtf.cxx b/vcl/source/gdi/gdimtf.cxx
new file mode 100644
index 0000000000..fb4ff37dfa
--- /dev/null
+++ b/vcl/source/gdi/gdimtf.cxx
@@ -0,0 +1,2357 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cstdlib>
+#include <memory>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/helpers.hxx>
+#include <tools/stream.hxx>
+#include <tools/vcompat.hxx>
+#include <tools/fract.hxx>
+#include <vcl/BitmapPalette.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/window.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/graphictools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/mtfxmldump.hxx>
+
+#include <vcl/TypeSerializer.hxx>
+
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/rendering/MtfRenderer.hpp>
+#include <com/sun/star/rendering/XBitmapCanvas.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+#include <comphelper/processfactory.hxx>
+
+using namespace com::sun::star;
+
+namespace {
+
+struct ImplColAdjustParam
+{
+ std::unique_ptr<sal_uInt8[]> pMapR;
+ std::unique_ptr<sal_uInt8[]> pMapG;
+ std::unique_ptr<sal_uInt8[]> pMapB;
+};
+
+struct ImplBmpAdjustParam
+{
+ short nLuminancePercent;
+ short nContrastPercent;
+ short nChannelRPercent;
+ short nChannelGPercent;
+ short nChannelBPercent;
+ double fGamma;
+ bool bInvert;
+};
+
+struct ImplColConvertParam
+{
+ MtfConversion eConversion;
+};
+
+struct ImplBmpConvertParam
+{
+ BmpConversion eConversion;
+};
+
+struct ImplColMonoParam
+{
+ Color aColor;
+};
+
+struct ImplBmpMonoParam
+{
+ Color aColor;
+};
+
+struct ImplColReplaceParam
+{
+ std::unique_ptr<sal_uLong[]> pMinR;
+ std::unique_ptr<sal_uLong[]> pMaxR;
+ std::unique_ptr<sal_uLong[]> pMinG;
+ std::unique_ptr<sal_uLong[]> pMaxG;
+ std::unique_ptr<sal_uLong[]> pMinB;
+ std::unique_ptr<sal_uLong[]> pMaxB;
+ const Color * pDstCols;
+ sal_uLong nCount;
+};
+
+struct ImplBmpReplaceParam
+{
+ const Color* pSrcCols;
+ const Color* pDstCols;
+ sal_uLong nCount;
+};
+
+}
+
+GDIMetaFile::GDIMetaFile() :
+ m_nCurrentActionElement( 0 ),
+ m_aPrefSize ( 1, 1 ),
+ m_pPrev ( nullptr ),
+ m_pNext ( nullptr ),
+ m_pOutDev ( nullptr ),
+ m_bPause ( false ),
+ m_bRecord ( false ),
+ m_bUseCanvas ( false ),
+ m_bSVG ( false )
+{
+}
+
+GDIMetaFile::GDIMetaFile( const GDIMetaFile& rMtf ) :
+ m_nCurrentActionElement( rMtf.m_nCurrentActionElement ),
+ m_aPrefMapMode ( rMtf.m_aPrefMapMode ),
+ m_aPrefSize ( rMtf.m_aPrefSize ),
+ m_pPrev ( rMtf.m_pPrev ),
+ m_pNext ( rMtf.m_pNext ),
+ m_pOutDev ( nullptr ),
+ m_bPause ( false ),
+ m_bRecord ( false ),
+ m_bUseCanvas ( rMtf.m_bUseCanvas ),
+ m_bSVG ( rMtf.m_bSVG )
+{
+ for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
+ {
+ m_aList.push_back( rMtf.GetAction( i ) );
+ }
+
+ if( rMtf.m_bRecord )
+ {
+ Record( rMtf.m_pOutDev );
+
+ if ( rMtf.m_bPause )
+ Pause( true );
+ }
+}
+
+GDIMetaFile::~GDIMetaFile()
+{
+ Clear();
+}
+
+bool GDIMetaFile::HasTransparentActions() const
+{
+ MetaAction* pCurrAct;
+
+ // watch for transparent drawing actions
+ for(pCurrAct = const_cast<GDIMetaFile*>(this)->FirstAction();
+ pCurrAct;
+ pCurrAct = const_cast<GDIMetaFile*>(this)->NextAction())
+ {
+ // #i10613# determine if the action is transparency capable
+
+ // #107169# Also examine metafiles with masked bitmaps in
+ // detail. Further down, this is optimized in such a way
+ // that there's no unnecessary painting of masked bitmaps
+ // (which are _always_ subdivided into rectangular regions
+ // of uniform opacity): if a masked bitmap is printed over
+ // empty background, we convert to a plain bitmap with
+ // white background.
+ if (pCurrAct->IsTransparent())
+ return true;
+ }
+
+ return false;
+}
+
+size_t GDIMetaFile::GetActionSize() const
+{
+ return m_aList.size();
+}
+
+MetaAction* GDIMetaFile::GetAction( size_t nAction ) const
+{
+ return (nAction < m_aList.size()) ? m_aList[ nAction ].get() : nullptr;
+}
+
+MetaAction* GDIMetaFile::FirstAction()
+{
+ m_nCurrentActionElement = 0;
+ return m_aList.empty() ? nullptr : m_aList[ 0 ].get();
+}
+
+MetaAction* GDIMetaFile::NextAction()
+{
+ return ( m_nCurrentActionElement + 1 < m_aList.size() ) ? m_aList[ ++m_nCurrentActionElement ].get() : nullptr;
+}
+
+void GDIMetaFile::ReplaceAction( rtl::Reference<MetaAction> pAction, size_t nAction )
+{
+ if ( nAction >= m_aList.size() )
+ {
+ return;
+ }
+ //fdo#39995 This doesn't increment the incoming action ref-count nor does it
+ //decrement the outgoing action ref-count
+ std::swap(pAction, m_aList[nAction]);
+}
+
+GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf )
+{
+ if( this != &rMtf )
+ {
+ Clear();
+
+ // Increment RefCount of MetaActions
+ for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
+ {
+ m_aList.push_back( rMtf.GetAction( i ) );
+ }
+
+ m_aPrefMapMode = rMtf.m_aPrefMapMode;
+ m_aPrefSize = rMtf.m_aPrefSize;
+ m_pPrev = rMtf.m_pPrev;
+ m_pNext = rMtf.m_pNext;
+ m_pOutDev = nullptr;
+ m_bPause = false;
+ m_bRecord = false;
+ m_bUseCanvas = rMtf.m_bUseCanvas;
+ m_bSVG = rMtf.m_bSVG;
+
+ if( rMtf.m_bRecord )
+ {
+ Record( rMtf.m_pOutDev );
+
+ if( rMtf.m_bPause )
+ Pause( true );
+ }
+ }
+
+ return *this;
+}
+
+bool GDIMetaFile::operator==( const GDIMetaFile& rMtf ) const
+{
+ const size_t nObjCount = m_aList.size();
+ bool bRet = false;
+
+ if( this == &rMtf )
+ bRet = true;
+ else if( rMtf.GetActionSize() == nObjCount &&
+ rMtf.GetPrefSize() == m_aPrefSize &&
+ rMtf.GetPrefMapMode() == m_aPrefMapMode )
+ {
+ bRet = true;
+
+ for( size_t n = 0; n < nObjCount; n++ )
+ {
+ if( m_aList[ n ] != rMtf.GetAction( n ) )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void GDIMetaFile::Clear()
+{
+ if( m_bRecord )
+ Stop();
+
+ m_aList.clear();
+}
+
+void GDIMetaFile::Linker( OutputDevice* pOut, bool bLink )
+{
+ if( bLink )
+ {
+ m_pNext = nullptr;
+ m_pPrev = pOut->GetConnectMetaFile();
+ pOut->SetConnectMetaFile( this );
+
+ if( m_pPrev )
+ m_pPrev->m_pNext = this;
+ }
+ else
+ {
+ if( m_pNext )
+ {
+ m_pNext->m_pPrev = m_pPrev;
+
+ if( m_pPrev )
+ m_pPrev->m_pNext = m_pNext;
+ }
+ else
+ {
+ if( m_pPrev )
+ m_pPrev->m_pNext = nullptr;
+
+ pOut->SetConnectMetaFile( m_pPrev );
+ }
+
+ m_pPrev = nullptr;
+ m_pNext = nullptr;
+ }
+}
+
+void GDIMetaFile::Record( OutputDevice* pOut )
+{
+ if( m_bRecord )
+ Stop();
+
+ m_nCurrentActionElement = m_aList.empty() ? 0 : (m_aList.size() - 1);
+ m_pOutDev = pOut;
+ m_bRecord = true;
+ Linker( pOut, true );
+}
+
+void GDIMetaFile::Play( GDIMetaFile& rMtf )
+{
+ if (m_bRecord || rMtf.m_bRecord)
+ return;
+
+ MetaAction* pAction = GetCurAction();
+ const size_t nObjCount = m_aList.size();
+
+ rMtf.UseCanvas( rMtf.GetUseCanvas() || m_bUseCanvas );
+ rMtf.setSVG( rMtf.getSVG() || m_bSVG );
+
+ for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nObjCount; nCurPos++ )
+ {
+ if( pAction )
+ {
+ rMtf.AddAction( pAction );
+ }
+
+ pAction = NextAction();
+ }
+}
+
+void GDIMetaFile::Play(OutputDevice& rOut, size_t nPos)
+{
+ if( m_bRecord )
+ return;
+
+ MetaAction* pAction = GetCurAction();
+ const size_t nObjCount = m_aList.size();
+ size_t nSyncCount = rOut.GetSyncCount();
+
+ 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 explicitly set
+ // those states.
+ rOut.Push(vcl::PushFlags::TEXTLAYOUTMODE|vcl::PushFlags::TEXTLANGUAGE);
+ rOut.SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+ rOut.SetDigitLanguage(LANGUAGE_SYSTEM);
+
+ SAL_INFO( "vcl.gdi", "GDIMetaFile::Play on device of size: " << rOut.GetOutputSizePixel().Width() << " " << rOut.GetOutputSizePixel().Height());
+
+ if (!ImplPlayWithRenderer(rOut, Point(0,0), rOut.GetOutputSize())) {
+ size_t i = 0;
+ for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nPos; nCurPos++ )
+ {
+ if( pAction )
+ {
+ pAction->Execute(&rOut);
+
+ // flush output from time to time
+ if( i++ > nSyncCount )
+ {
+ rOut.Flush();
+ i = 0;
+ }
+ }
+
+ pAction = NextAction();
+ }
+ }
+ rOut.Pop();
+}
+
+bool GDIMetaFile::ImplPlayWithRenderer(OutputDevice& rOut, const Point& rPos, Size rLogicDestSize)
+{
+ if (!m_bUseCanvas)
+ return false;
+
+ Size rDestSize(rOut.LogicToPixel(rLogicDestSize));
+
+ const vcl::Window* win = rOut.GetOwnerWindow();
+
+ if (!win)
+ win = Application::GetActiveTopWindow();
+ if (!win)
+ win = Application::GetFirstTopLevelWindow();
+
+ if (!win)
+ return false;
+
+ try
+ {
+ uno::Reference<rendering::XCanvas> xCanvas = win->GetOutDev()->GetCanvas ();
+
+ if (!xCanvas.is())
+ return false;
+
+ Size aSize (rDestSize.Width () + 1, rDestSize.Height () + 1);
+ uno::Reference<rendering::XBitmap> xBitmap = xCanvas->getDevice ()->createCompatibleAlphaBitmap (vcl::unotools::integerSize2DFromSize( aSize));
+ if( xBitmap.is () )
+ {
+ uno::Reference< rendering::XBitmapCanvas > xBitmapCanvas( xBitmap, uno::UNO_QUERY );
+ if( xBitmapCanvas.is() )
+ {
+ uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ uno::Reference< rendering::XMtfRenderer > xMtfRenderer = rendering::MtfRenderer::createWithBitmapCanvas( xContext, xBitmapCanvas );
+
+ xBitmapCanvas->clear();
+ uno::Reference< beans::XFastPropertySet > xMtfFastPropertySet( xMtfRenderer, uno::UNO_QUERY );
+ if( xMtfFastPropertySet.is() )
+ // set this metafile to the renderer to
+ // speedup things (instead of copying data to
+ // sequence of bytes passed to renderer)
+ xMtfFastPropertySet->setFastPropertyValue( 0, uno::Any( reinterpret_cast<sal_Int64>( this ) ) );
+
+ xMtfRenderer->draw( rDestSize.Width(), rDestSize.Height() );
+
+ BitmapEx aBitmapEx;
+ if( aBitmapEx.Create( xBitmapCanvas, aSize ) )
+ {
+ if (rOut.GetMapMode().GetMapUnit() == MapUnit::MapPixel)
+ rOut.DrawBitmapEx( rPos, aBitmapEx );
+ else
+ rOut.DrawBitmapEx( rPos, rLogicDestSize, aBitmapEx );
+ return true;
+ }
+ }
+ }
+ }
+ catch (const uno::RuntimeException& )
+ {
+ throw; // runtime errors are fatal
+ }
+ catch (const uno::Exception&)
+ {
+ // ignore errors, no way of reporting them here
+ TOOLS_WARN_EXCEPTION("vcl.gdi", "GDIMetaFile::ImplPlayWithRenderer");
+ }
+
+ return false;
+}
+
+void GDIMetaFile::Play(OutputDevice& rOut, const Point& rPos,
+ const Size& rSize)
+{
+ MapMode aDrawMap( GetPrefMapMode() );
+ Size aDestSize(rOut.LogicToPixel(rSize));
+
+ if (aDestSize.Width() <= 0 || aDestSize.Height() <= 0)
+ return;
+
+ if (aDestSize.Width() > std::numeric_limits<sal_Int32>::max() ||
+ aDestSize.Height() > std::numeric_limits<sal_Int32>::max())
+ return;
+
+ GDIMetaFile* pMtf = rOut.GetConnectMetaFile();
+
+ if (ImplPlayWithRenderer(rOut, rPos, rSize))
+ return;
+
+ Size aTmpPrefSize(rOut.LogicToPixel(GetPrefSize(), aDrawMap));
+
+ if( !aTmpPrefSize.Width() )
+ aTmpPrefSize.setWidth( aDestSize.Width() );
+
+ if( !aTmpPrefSize.Height() )
+ aTmpPrefSize.setHeight( aDestSize.Height() );
+
+ Fraction aScaleX( aDestSize.Width(), aTmpPrefSize.Width() );
+ Fraction aScaleY( aDestSize.Height(), aTmpPrefSize.Height() );
+
+ aScaleX *= aDrawMap.GetScaleX();
+ aScaleY *= aDrawMap.GetScaleY();
+ // try reducing inaccurary first and abandon if the scaling
+ // still cannot be achieved
+ if (TooLargeScaleForMapMode(aScaleX, rOut.GetDPIX()))
+ aScaleX.ReduceInaccurate(10);
+ if (TooLargeScaleForMapMode(aScaleY, rOut.GetDPIY()))
+ aScaleY.ReduceInaccurate(10);
+ if (TooLargeScaleForMapMode(aScaleX, rOut.GetDPIX()) ||
+ TooLargeScaleForMapMode(aScaleY, rOut.GetDPIY()))
+ {
+ SAL_WARN("vcl", "GDIMetaFile Scaling is too high");
+ return;
+ }
+
+ aDrawMap.SetScaleX(aScaleX);
+ 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 rOut, 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(rOut.GetPixelOffset());
+ const Size aEmptySize;
+ rOut.SetPixelOffset(aEmptySize);
+ aDrawMap.SetOrigin(rOut.PixelToLogic(rOut.LogicToPixel(rPos), aDrawMap));
+ rOut.SetPixelOffset(rOldOffset);
+
+ rOut.Push();
+
+ bool bIsRecord = (pMtf && pMtf->IsRecord());
+ rOut.SetMetafileMapMode(aDrawMap, bIsRecord);
+
+ // #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 explicitly set
+ // those states.
+ rOut.SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+ rOut.SetDigitLanguage(LANGUAGE_SYSTEM);
+
+ Play(rOut);
+
+ rOut.Pop();
+}
+
+void GDIMetaFile::Pause( bool _bPause )
+{
+ if( !m_bRecord )
+ return;
+
+ if( _bPause )
+ {
+ if( !m_bPause )
+ Linker( m_pOutDev, false );
+ }
+ else
+ {
+ if( m_bPause )
+ Linker( m_pOutDev, true );
+ }
+
+ m_bPause = _bPause;
+}
+
+void GDIMetaFile::Stop()
+{
+ if( m_bRecord )
+ {
+ m_bRecord = false;
+
+ if( !m_bPause )
+ Linker( m_pOutDev, false );
+ else
+ m_bPause = false;
+ }
+}
+
+void GDIMetaFile::WindStart()
+{
+ if( !m_bRecord )
+ m_nCurrentActionElement = 0;
+}
+
+void GDIMetaFile::WindPrev()
+{
+ if( !m_bRecord )
+ if ( m_nCurrentActionElement > 0 )
+ --m_nCurrentActionElement;
+}
+
+void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction)
+{
+ m_aList.push_back( pAction );
+
+ if( m_pPrev )
+ {
+ m_pPrev->AddAction( pAction );
+ }
+}
+
+void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction, size_t nPos)
+{
+ if ( nPos < m_aList.size() )
+ {
+ m_aList.insert( m_aList.begin() + nPos, pAction );
+ }
+ else
+ {
+ m_aList.push_back( pAction );
+ }
+
+ if( m_pPrev )
+ {
+ m_pPrev->AddAction( pAction, nPos );
+ }
+}
+
+void GDIMetaFile::push_back(const rtl::Reference<MetaAction>& pAction)
+{
+ m_aList.push_back( pAction );
+}
+
+void GDIMetaFile::Mirror( BmpMirrorFlags nMirrorFlags )
+{
+ const Size aOldPrefSize( GetPrefSize() );
+ tools::Long nMoveX, nMoveY;
+ double fScaleX, fScaleY;
+
+ if( nMirrorFlags & BmpMirrorFlags::Horizontal )
+ {
+ nMoveX = std::abs( aOldPrefSize.Width() ) - 1;
+ fScaleX = -1.0;
+ }
+ else
+ {
+ nMoveX = 0;
+ fScaleX = 1.0;
+ }
+
+ if( nMirrorFlags & BmpMirrorFlags::Vertical )
+ {
+ nMoveY = std::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 );
+ }
+}
+
+void GDIMetaFile::Move( tools::Long nX, tools::Long nY )
+{
+ const Size aBaseOffset( nX, nY );
+ Size aOffset( aBaseOffset );
+ ScopedVclPtrInstance< VirtualDevice > aMapVDev;
+
+ aMapVDev->EnableOutput( false );
+ aMapVDev->SetMapMode( GetPrefMapMode() );
+
+ for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
+ {
+ const MetaActionType nType = pAct->GetType();
+ MetaAction* pModAct;
+
+ if( pAct->GetRefCount() > 1 )
+ {
+ m_aList[ m_nCurrentActionElement ] = pAct->Clone();
+ pModAct = m_aList[ m_nCurrentActionElement ].get();
+ }
+ else
+ pModAct = pAct;
+
+ if( ( MetaActionType::MAPMODE == nType ) ||
+ ( MetaActionType::PUSH == nType ) ||
+ ( MetaActionType::POP == nType ) )
+ {
+ pModAct->Execute( aMapVDev.get() );
+ aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
+ }
+
+ pModAct->Move( aOffset.Width(), aOffset.Height() );
+ }
+}
+
+void GDIMetaFile::Move( tools::Long nX, tools::Long nY, tools::Long nDPIX, tools::Long nDPIY )
+{
+ const Size aBaseOffset( nX, nY );
+ Size aOffset( aBaseOffset );
+ ScopedVclPtrInstance< VirtualDevice > aMapVDev;
+
+ aMapVDev->EnableOutput( false );
+ aMapVDev->SetReferenceDevice( nDPIX, nDPIY );
+ aMapVDev->SetMapMode( GetPrefMapMode() );
+
+ for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
+ {
+ const MetaActionType nType = pAct->GetType();
+ MetaAction* pModAct;
+
+ if( pAct->GetRefCount() > 1 )
+ {
+ m_aList[ m_nCurrentActionElement ] = pAct->Clone();
+ pModAct = m_aList[ m_nCurrentActionElement ].get();
+ }
+ else
+ pModAct = pAct;
+
+ if( ( MetaActionType::MAPMODE == nType ) ||
+ ( MetaActionType::PUSH == nType ) ||
+ ( MetaActionType::POP == nType ) )
+ {
+ pModAct->Execute( aMapVDev.get() );
+ if( aMapVDev->GetMapMode().GetMapUnit() == MapUnit::MapPixel )
+ {
+ aOffset = aMapVDev->LogicToPixel( aBaseOffset, GetPrefMapMode() );
+ MapMode aMap( aMapVDev->GetMapMode() );
+ aOffset.setWidth( static_cast<tools::Long>(aOffset.Width() * static_cast<double>(aMap.GetScaleX())) );
+ aOffset.setHeight( static_cast<tools::Long>(aOffset.Height() * static_cast<double>(aMap.GetScaleY())) );
+ }
+ else
+ aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
+ }
+
+ pModAct->Move( aOffset.Width(), aOffset.Height() );
+ }
+}
+
+void GDIMetaFile::ScaleActions(double const fScaleX, double const fScaleY)
+{
+ for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
+ {
+ MetaAction* pModAct;
+
+ if( pAct->GetRefCount() > 1 )
+ {
+ m_aList[ m_nCurrentActionElement ] = pAct->Clone();
+ pModAct = m_aList[ m_nCurrentActionElement ].get();
+ }
+ else
+ pModAct = pAct;
+
+ pModAct->Scale( fScaleX, fScaleY );
+ }
+}
+
+void GDIMetaFile::Scale( double fScaleX, double fScaleY )
+{
+ ScaleActions(fScaleX, fScaleY);
+
+ m_aPrefSize.setWidth( FRound( m_aPrefSize.Width() * fScaleX ) );
+ m_aPrefSize.setHeight( FRound( m_aPrefSize.Height() * fScaleY ) );
+}
+
+void GDIMetaFile::Scale( const Fraction& rScaleX, const Fraction& rScaleY )
+{
+ Scale( static_cast<double>(rScaleX), static_cast<double>(rScaleY) );
+}
+
+void GDIMetaFile::Clip( const tools::Rectangle& i_rClipRect )
+{
+ tools::Rectangle aCurRect( i_rClipRect );
+ ScopedVclPtrInstance< VirtualDevice > aMapVDev;
+
+ aMapVDev->EnableOutput( false );
+ aMapVDev->SetMapMode( GetPrefMapMode() );
+
+ for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
+ {
+ const MetaActionType nType = pAct->GetType();
+
+ if( ( MetaActionType::MAPMODE == nType ) ||
+ ( MetaActionType::PUSH == nType ) ||
+ ( MetaActionType::POP == nType ) )
+ {
+ pAct->Execute( aMapVDev.get() );
+ aCurRect = OutputDevice::LogicToLogic( i_rClipRect, GetPrefMapMode(), aMapVDev->GetMapMode() );
+ }
+ else if( nType == MetaActionType::CLIPREGION )
+ {
+ MetaClipRegionAction* pOldAct = static_cast<MetaClipRegionAction*>(pAct);
+ vcl::Region aNewReg( aCurRect );
+ if( pOldAct->IsClipping() )
+ aNewReg.Intersect( pOldAct->GetRegion() );
+ MetaClipRegionAction* pNewAct = new MetaClipRegionAction( std::move(aNewReg), true );
+ m_aList[ m_nCurrentActionElement ] = pNewAct;
+ }
+ }
+}
+
+Point GDIMetaFile::ImplGetRotatedPoint( const Point& rPt, const Point& rRotatePt,
+ const Size& rOffset, double fSin, double fCos )
+{
+ const tools::Long nX = rPt.X() - rRotatePt.X();
+ const tools::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() );
+}
+
+tools::Polygon GDIMetaFile::ImplGetRotatedPolygon( const tools::Polygon& rPoly, const Point& rRotatePt,
+ const Size& rOffset, double fSin, double fCos )
+{
+ tools::Polygon aRet( rPoly );
+
+ aRet.Rotate( rRotatePt, fSin, fCos );
+ aRet.Move( rOffset.Width(), rOffset.Height() );
+
+ return aRet;
+}
+
+tools::PolyPolygon GDIMetaFile::ImplGetRotatedPolyPolygon( const tools::PolyPolygon& rPolyPoly, const Point& rRotatePt,
+ const Size& rOffset, double fSin, double fCos )
+{
+ tools::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 tools::PolyPolygon& rPolyPoly,
+ const Gradient& rGrad )
+{
+ // Generate comment, GradientEx and Gradient actions (within DrawGradient)
+ ScopedVclPtrInstance< VirtualDevice > aVDev(rMapDev, DeviceFormat::WITHOUT_ALPHA);
+ aVDev->EnableOutput( false );
+ GDIMetaFile aGradMtf;
+
+ aGradMtf.Record( aVDev.get() );
+ aVDev->DrawGradient( rPolyPoly, rGrad );
+ aGradMtf.Stop();
+
+ size_t i, nAct( aGradMtf.GetActionSize() );
+ for( i=0; i < nAct; ++i )
+ {
+ MetaAction* pMetaAct = aGradMtf.GetAction( i );
+ rMtf.AddAction( pMetaAct );
+ }
+}
+
+void GDIMetaFile::Rotate( Degree10 nAngle10 )
+{
+ nAngle10 %= 3600_deg10;
+ nAngle10 = ( nAngle10 < 0_deg10 ) ? ( Degree10(3599) + nAngle10 ) : nAngle10;
+
+ if( !nAngle10 )
+ return;
+
+ GDIMetaFile aMtf;
+ ScopedVclPtrInstance< VirtualDevice > aMapVDev;
+ const double fAngle = toRadians(nAngle10);
+ const double fSin = sin( fAngle );
+ const double fCos = cos( fAngle );
+ tools::Rectangle aRect( Point(), GetPrefSize() );
+ tools::Polygon aPoly( aRect );
+
+ aPoly.Rotate( Point(), fSin, fCos );
+
+ aMapVDev->EnableOutput( false );
+ aMapVDev->SetMapMode( GetPrefMapMode() );
+
+ const tools::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 = FirstAction(); pAction; pAction = NextAction() )
+ {
+ const MetaActionType nActionType = pAction->GetType();
+
+ switch( nActionType )
+ {
+ case MetaActionType::PIXEL:
+ {
+ MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
+ aMtf.AddAction( new MetaPixelAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetColor() ) );
+ }
+ break;
+
+ case MetaActionType::POINT:
+ {
+ MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
+ aMtf.AddAction( new MetaPointAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::LINE:
+ {
+ MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
+ aMtf.AddAction( new MetaLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
+ ImplGetRotatedPoint( pAct->GetEndPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetLineInfo() ) );
+ }
+ break;
+
+ case MetaActionType::RECT:
+ {
+ MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
+ aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( tools::Polygon(pAct->GetRect()), aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::ROUNDRECT:
+ {
+ MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
+ const tools::Polygon aRoundRectPoly( pAct->GetRect(), pAct->GetHorzRound(), pAct->GetVertRound() );
+
+ aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aRoundRectPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::ELLIPSE:
+ {
+ MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
+ const tools::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 MetaActionType::ARC:
+ {
+ MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
+ const tools::Polygon aArcPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Arc );
+
+ aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aArcPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::PIE:
+ {
+ MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
+ const tools::Polygon aPiePoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Pie );
+
+ aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aPiePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::CHORD:
+ {
+ MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
+ const tools::Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Chord );
+
+ aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aChordPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::POLYLINE:
+ {
+ MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
+ aMtf.AddAction( new MetaPolyLineAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->GetLineInfo() ) );
+ }
+ break;
+
+ case MetaActionType::POLYGON:
+ {
+ MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
+ aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::POLYPOLYGON:
+ {
+ MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
+ aMtf.AddAction( new MetaPolyPolygonAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
+ }
+ break;
+
+ case MetaActionType::TEXT:
+ {
+ MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
+ aMtf.AddAction( new MetaTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
+ }
+ break;
+
+ case MetaActionType::TEXTARRAY:
+ {
+ MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
+ aMtf.AddAction( new MetaTextArrayAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetText(), pAct->GetDXArray(), pAct->GetKashidaArray(), pAct->GetIndex(), pAct->GetLen() ) );
+ }
+ break;
+
+ case MetaActionType::STRETCHTEXT:
+ {
+ MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
+ aMtf.AddAction( new MetaStretchTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetWidth(), pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
+ }
+ break;
+
+ case MetaActionType::TEXTLINE:
+ {
+ MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
+ aMtf.AddAction( new MetaTextLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetWidth(), pAct->GetStrikeout(), pAct->GetUnderline(), pAct->GetOverline() ) );
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
+ tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
+ tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
+ BitmapEx aBmpEx( pAct->GetBitmap() );
+
+ aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
+ aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(),
+ aBmpEx ) );
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
+ tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
+ tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
+ BitmapEx aBmpEx( pAct->GetBitmap() );
+
+ aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
+ aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
+
+ aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
+ tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
+ tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
+ BitmapEx aBmpEx( pAct->GetBitmapEx() );
+
+ aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
+
+ aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
+ tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
+ tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
+ BitmapEx aBmpEx( pAct->GetBitmapEx() );
+
+ aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
+ aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
+
+ aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
+ }
+ break;
+
+ case MetaActionType::GRADIENT:
+ {
+ MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
+
+ ImplAddGradientEx( aMtf, *aMapVDev,
+ tools::PolyPolygon(ImplGetRotatedPolygon( tools::Polygon(pAct->GetRect()), aRotAnchor, aRotOffset, fSin, fCos )),
+ pAct->GetGradient() );
+ }
+ break;
+
+ case MetaActionType::GRADIENTEX:
+ {
+ MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
+ aMtf.AddAction( new MetaGradientExAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetGradient() ) );
+ }
+ break;
+
+ // Handle gradientex comment block correctly
+ case MetaActionType::COMMENT:
+ {
+ MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction);
+ if( pCommentAct->GetComment() == "XGRAD_SEQ_BEGIN" )
+ {
+ int nBeginComments( 1 );
+ pAction = NextAction();
+
+ // skip everything, except gradientex action
+ while( pAction )
+ {
+ const MetaActionType nType = pAction->GetType();
+
+ if( MetaActionType::GRADIENTEX == nType )
+ {
+ // Add rotated gradientex
+ MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
+ ImplAddGradientEx( aMtf, *aMapVDev,
+ ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetGradient() );
+ }
+ else if( MetaActionType::COMMENT == nType)
+ {
+ MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pAction);
+ if( pAct->GetComment() == "XGRAD_SEQ_END" )
+ {
+ // handle nested blocks
+ --nBeginComments;
+
+ // gradientex comment block: end reached, done.
+ if( !nBeginComments )
+ break;
+ }
+ else if( pAct->GetComment() == "XGRAD_SEQ_BEGIN" )
+ {
+ // handle nested blocks
+ ++nBeginComments;
+ }
+
+ }
+
+ pAction =NextAction();
+ }
+ }
+ else
+ {
+ bool bPathStroke = (pCommentAct->GetComment() == "XPATHSTROKE_SEQ_BEGIN");
+ if ( bPathStroke || pCommentAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
+ {
+ if ( pCommentAct->GetDataSize() )
+ {
+ SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pCommentAct->GetData()), pCommentAct->GetDataSize(), StreamMode::READ );
+ SvMemoryStream aDest;
+ if ( bPathStroke )
+ {
+ SvtGraphicStroke aStroke;
+ ReadSvtGraphicStroke( aMemStm, aStroke );
+ tools::Polygon aPath;
+ aStroke.getPath( aPath );
+ aStroke.setPath( ImplGetRotatedPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
+ WriteSvtGraphicStroke( aDest, aStroke );
+ aMtf.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN"_ostr, 0,
+ static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
+ }
+ else
+ {
+ SvtGraphicFill aFill;
+ ReadSvtGraphicFill( aMemStm, aFill );
+ tools::PolyPolygon aPath;
+ aFill.getPath( aPath );
+ aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
+ WriteSvtGraphicFill( aDest, aFill );
+ aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN"_ostr, 0,
+ static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
+ }
+ }
+ }
+ else if ( pCommentAct->GetComment() == "XPATHSTROKE_SEQ_END"
+ || pCommentAct->GetComment() == "XPATHFILL_SEQ_END" )
+ {
+ pAction->Execute( aMapVDev.get() );
+ aMtf.AddAction( pAction );
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::HATCH:
+ {
+ MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
+ Hatch aHatch( pAct->GetHatch() );
+
+ aHatch.SetAngle( aHatch.GetAngle() + nAngle10 );
+ aMtf.AddAction( new MetaHatchAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
+ aHatch ) );
+ }
+ break;
+
+ case MetaActionType::Transparent:
+ {
+ MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
+ aMtf.AddAction( new MetaTransparentAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
+ pAct->GetTransparence() ) );
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
+ GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
+ tools::Polygon aMtfPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
+ tools::Rectangle aMtfRect( aMtfPoly.GetBoundRect() );
+
+ aTransMtf.Rotate( nAngle10 );
+ aMtf.AddAction( new MetaFloatTransparentAction( aTransMtf, aMtfRect.TopLeft(), aMtfRect.GetSize(),
+ pAct->GetGradient() ) );
+ }
+ break;
+
+ case MetaActionType::EPS:
+ {
+ MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
+ GDIMetaFile aEPSMtf( pAct->GetSubstitute() );
+ tools::Polygon aEPSPoly( ImplGetRotatedPolygon( tools::Polygon(tools::Rectangle( pAct->GetPoint(), pAct->GetSize() )), aRotAnchor, aRotOffset, fSin, fCos ) );
+ tools::Rectangle aEPSRect( aEPSPoly.GetBoundRect() );
+
+ aEPSMtf.Rotate( nAngle10 );
+ aMtf.AddAction( new MetaEPSAction( aEPSRect.TopLeft(), aEPSRect.GetSize(),
+ pAct->GetLink(), aEPSMtf ) );
+ }
+ break;
+
+ case MetaActionType::CLIPREGION:
+ {
+ MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
+
+ if( pAct->IsClipping() && pAct->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
+ aMtf.AddAction( new MetaClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( pAct->GetRegion().GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ), true ) );
+ else
+ {
+ aMtf.AddAction( pAction );
+ }
+ }
+ break;
+
+ case MetaActionType::ISECTRECTCLIPREGION:
+ {
+ MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
+ aMtf.AddAction( new MetaISectRegionClipRegionAction(vcl::Region(
+ ImplGetRotatedPolygon( tools::Polygon(pAct->GetRect()), aRotAnchor,
+ aRotOffset, fSin, fCos )) ) );
+ }
+ break;
+
+ case MetaActionType::ISECTREGIONCLIPREGION:
+ {
+ MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
+ const vcl::Region& rRegion = pAct->GetRegion();
+
+ if( rRegion.HasPolyPolygonOrB2DPolyPolygon() )
+ aMtf.AddAction( new MetaISectRegionClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( rRegion.GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ) );
+ else
+ {
+ aMtf.AddAction( pAction );
+ }
+ }
+ break;
+
+ case MetaActionType::REFPOINT:
+ {
+ MetaRefPointAction* pAct = static_cast<MetaRefPointAction*>(pAction);
+ aMtf.AddAction( new MetaRefPointAction( ImplGetRotatedPoint( pAct->GetRefPoint(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->IsSetting() ) );
+ }
+ break;
+
+ case MetaActionType::FONT:
+ {
+ MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
+ vcl::Font aFont( pAct->GetFont() );
+
+ aFont.SetOrientation( aFont.GetOrientation() + nAngle10 );
+ aMtf.AddAction( new MetaFontAction( std::move(aFont) ) );
+ }
+ break;
+
+ case MetaActionType::BMP:
+ case MetaActionType::BMPEX:
+ case MetaActionType::MASK:
+ case MetaActionType::MASKSCALE:
+ case MetaActionType::MASKSCALEPART:
+ case MetaActionType::WALLPAPER:
+ case MetaActionType::TEXTRECT:
+ case MetaActionType::MOVECLIPREGION:
+ {
+ OSL_FAIL( "GDIMetaFile::Rotate(): unsupported action" );
+ }
+ break;
+
+ default:
+ {
+ pAction->Execute( aMapVDev.get() );
+ aMtf.AddAction( pAction );
+
+ // update rotation point and offset, if necessary
+ if( ( MetaActionType::MAPMODE == nActionType ) ||
+ ( MetaActionType::PUSH == nActionType ) ||
+ ( MetaActionType::POP == nActionType ) )
+ {
+ aRotAnchor = OutputDevice::LogicToLogic( aOrigin, m_aPrefMapMode, aMapVDev->GetMapMode() );
+ aRotOffset = OutputDevice::LogicToLogic( aOffset, m_aPrefMapMode, aMapVDev->GetMapMode() );
+ }
+ }
+ break;
+ }
+ }
+
+ aMtf.m_aPrefMapMode = m_aPrefMapMode;
+ aMtf.m_aPrefSize = aNewBound.GetSize();
+
+ *this = aMtf;
+
+}
+
+static void ImplActionBounds( tools::Rectangle& o_rOutBounds,
+ const tools::Rectangle& i_rInBounds,
+ const std::vector<tools::Rectangle>& i_rClipStack )
+{
+ tools::Rectangle aBounds( i_rInBounds );
+ if( ! i_rInBounds.IsEmpty() && ! i_rClipStack.empty() && ! i_rClipStack.back().IsEmpty() )
+ aBounds.Intersection( i_rClipStack.back() );
+ if( aBounds.IsEmpty() )
+ return;
+
+ if( ! o_rOutBounds.IsEmpty() )
+ o_rOutBounds.Union( aBounds );
+ else
+ o_rOutBounds = aBounds;
+}
+
+tools::Rectangle GDIMetaFile::GetBoundRect( OutputDevice& i_rReference ) const
+{
+ ScopedVclPtrInstance< VirtualDevice > aMapVDev( i_rReference );
+
+ aMapVDev->EnableOutput( false );
+ aMapVDev->SetMapMode( GetPrefMapMode() );
+
+ std::vector<tools::Rectangle> aClipStack( 1, tools::Rectangle() );
+ std::vector<vcl::PushFlags> aPushFlagStack;
+
+ tools::Rectangle aBound;
+ const sal_uLong nCount(GetActionSize());
+
+ for(sal_uLong a(0); a < nCount; a++)
+ {
+ MetaAction* pAction = GetAction(a);
+ const MetaActionType nActionType = pAction->GetType();
+
+ switch( nActionType )
+ {
+ case MetaActionType::PIXEL:
+ {
+ MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
+ ImplActionBounds( aBound,
+ tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
+ aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
+ aClipStack );
+ }
+ break;
+
+ case MetaActionType::POINT:
+ {
+ MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
+ ImplActionBounds( aBound,
+ tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
+ aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
+ aClipStack );
+ }
+ break;
+
+ case MetaActionType::LINE:
+ {
+ MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
+ Point aP1( pAct->GetStartPoint() ), aP2( pAct->GetEndPoint() );
+ tools::Rectangle aRect( aP1, aP2 );
+ aRect.Normalize();
+
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::RECT:
+ {
+ MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::ROUNDRECT:
+ {
+ MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::ELLIPSE:
+ {
+ MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::ARC:
+ {
+ MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
+ // FIXME: this is imprecise
+ // e.g. for small arcs the whole rectangle is WAY too large
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::PIE:
+ {
+ MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
+ // FIXME: this is imprecise
+ // e.g. for small arcs the whole rectangle is WAY too large
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::CHORD:
+ {
+ MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
+ // FIXME: this is imprecise
+ // e.g. for small arcs the whole rectangle is WAY too large
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::POLYLINE:
+ {
+ MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
+
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::POLYGON:
+ {
+ MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::POLYPOLYGON:
+ {
+ MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::TEXT:
+ {
+ MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
+ tools::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, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::TEXTARRAY:
+ {
+ MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
+ tools::Rectangle aRect;
+ // hdu said base = index
+ aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
+ 0, pAct->GetDXArray(), pAct->GetKashidaArray() );
+ Point aPt( pAct->GetPoint() );
+ aRect.Move( aPt.X(), aPt.Y() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::STRETCHTEXT:
+ {
+ MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
+ tools::Rectangle aRect;
+ // hdu said base = index
+ aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
+ pAct->GetWidth() );
+ Point aPt( pAct->GetPoint() );
+ aRect.Move( aPt.X(), aPt.Y() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::TEXTLINE:
+ {
+ MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
+ // measure a test string to get ascend and descent right
+ static constexpr OUStringLiteral pStr = u"\u00c4g";
+ OUString aStr( pStr );
+
+ tools::Rectangle aRect;
+ aMapVDev->GetTextBoundRect( aRect, aStr, 0, 0, aStr.getLength() );
+ Point aPt( pAct->GetStartPoint() );
+ aRect.Move( aPt.X(), aPt.Y() );
+ aRect.SetRight( aRect.Left() + pAct->GetWidth() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::GRADIENT:
+ {
+ MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::GRADIENTEX:
+ {
+ MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::COMMENT:
+ {
+ // nothing to do
+ };
+ break;
+
+ case MetaActionType::HATCH:
+ {
+ MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::Transparent:
+ {
+ MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
+ // MetaFloatTransparentAction is defined limiting its content Metafile
+ // to its geometry definition(Point, Size), so use these directly
+ const tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::EPS:
+ {
+ MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::CLIPREGION:
+ {
+ MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
+ if( pAct->IsClipping() )
+ aClipStack.back() = OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() );
+ else
+ aClipStack.back() = tools::Rectangle();
+ }
+ break;
+
+ case MetaActionType::ISECTRECTCLIPREGION:
+ {
+ MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
+ tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
+ if( aClipStack.back().IsEmpty() )
+ aClipStack.back() = aRect;
+ else
+ aClipStack.back().Intersection( aRect );
+ }
+ break;
+
+ case MetaActionType::ISECTREGIONCLIPREGION:
+ {
+ MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
+ tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
+ if( aClipStack.back().IsEmpty() )
+ aClipStack.back() = aRect;
+ else
+ aClipStack.back().Intersection( aRect );
+ }
+ break;
+
+ case MetaActionType::BMP:
+ {
+ MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::BMPEX:
+ {
+ MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmapEx().GetSizePixel() ) );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::MASK:
+ {
+ MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::MASKSCALE:
+ {
+ MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::MASKSCALEPART:
+ {
+ MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::WALLPAPER:
+ {
+ MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::TEXTRECT:
+ {
+ MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pAction);
+ tools::Rectangle aRect( pAct->GetRect() );
+ ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
+ }
+ break;
+
+ case MetaActionType::MOVECLIPREGION:
+ {
+ MetaMoveClipRegionAction* pAct = static_cast<MetaMoveClipRegionAction*>(pAction);
+ if( ! aClipStack.back().IsEmpty() )
+ {
+ Size aDelta( pAct->GetHorzMove(), pAct->GetVertMove() );
+ aDelta = OutputDevice::LogicToLogic( aDelta, aMapVDev->GetMapMode(), GetPrefMapMode() );
+ aClipStack.back().Move( aDelta.Width(), aDelta.Width() );
+ }
+ }
+ break;
+
+ default:
+ {
+ pAction->Execute( aMapVDev.get() );
+
+ if( nActionType == MetaActionType::PUSH )
+ {
+ MetaPushAction* pAct = static_cast<MetaPushAction*>(pAction);
+ aPushFlagStack.push_back( pAct->GetFlags() );
+ if( aPushFlagStack.back() & vcl::PushFlags::CLIPREGION )
+ {
+ tools::Rectangle aRect( aClipStack.back() );
+ aClipStack.push_back( aRect );
+ }
+ }
+ else if( nActionType == MetaActionType::POP )
+ {
+ // sanity check
+ if( ! aPushFlagStack.empty() )
+ {
+ if( aPushFlagStack.back() & vcl::PushFlags::CLIPREGION )
+ {
+ if( aClipStack.size() > 1 )
+ aClipStack.pop_back();
+ }
+ aPushFlagStack.pop_back();
+ }
+ }
+ }
+ break;
+ }
+ }
+ return aBound;
+}
+
+Color GDIMetaFile::ImplColAdjustFnc( const Color& rColor, const void* pColParam )
+{
+ return Color( ColorAlpha, rColor.GetAlpha(),
+ static_cast<const ImplColAdjustParam*>(pColParam)->pMapR[ rColor.GetRed() ],
+ static_cast<const ImplColAdjustParam*>(pColParam)->pMapG[ rColor.GetGreen() ],
+ static_cast<const ImplColAdjustParam*>(pColParam)->pMapB[ rColor.GetBlue() ] );
+
+}
+
+BitmapEx GDIMetaFile::ImplBmpAdjustFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
+{
+ const ImplBmpAdjustParam* p = static_cast<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( MtfConversion::N1BitThreshold == static_cast<const ImplColConvertParam*>(pColParam)->eConversion )
+ cLum = ( cLum < 128 ) ? 0 : 255;
+
+ return Color( ColorAlpha, rColor.GetAlpha(), cLum, cLum, cLum );
+}
+
+BitmapEx GDIMetaFile::ImplBmpConvertFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
+{
+ BitmapEx aRet( rBmpEx );
+
+ aRet.Convert( static_cast<const ImplBmpConvertParam*>(pBmpParam)->eConversion );
+
+ return aRet;
+}
+
+Color GDIMetaFile::ImplColMonoFnc( const Color&, const void* pColParam )
+{
+ return static_cast<const ImplColMonoParam*>(pColParam)->aColor;
+}
+
+BitmapEx GDIMetaFile::ImplBmpMonoFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
+{
+ BitmapPalette aPal( 3 );
+ aPal[ 0 ] = COL_BLACK;
+ aPal[ 1 ] = COL_WHITE;
+ aPal[ 2 ] = static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor;
+
+ Bitmap aBmp(rBmpEx.GetSizePixel(), vcl::PixelFormat::N8_BPP, &aPal);
+ aBmp.Erase( static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor );
+
+ if( rBmpEx.IsAlpha() )
+ return BitmapEx( aBmp, rBmpEx.GetAlphaMask() );
+ else
+ return BitmapEx( 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 < static_cast<const ImplColReplaceParam*>(pColParam)->nCount; i++ )
+ {
+ if( ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinR[ i ] <= nR ) &&
+ ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxR[ i ] >= nR ) &&
+ ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinG[ i ] <= nG ) &&
+ ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxG[ i ] >= nG ) &&
+ ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinB[ i ] <= nB ) &&
+ ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxB[ i ] >= nB ) )
+ {
+ return static_cast<const ImplColReplaceParam*>(pColParam)->pDstCols[ i ];
+ }
+ }
+
+ return rColor;
+}
+
+BitmapEx GDIMetaFile::ImplBmpReplaceFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
+{
+ const ImplBmpReplaceParam* p = static_cast<const ImplBmpReplaceParam*>(pBmpParam);
+ BitmapEx aRet( rBmpEx );
+
+ aRet.Replace( p->pSrcCols, p->pDstCols, p->nCount );
+
+ return aRet;
+}
+
+void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pColParam,
+ BmpExchangeFnc pFncBmp, const void* pBmpParam )
+{
+ GDIMetaFile aMtf;
+
+ aMtf.m_aPrefSize = m_aPrefSize;
+ aMtf.m_aPrefMapMode = m_aPrefMapMode;
+ aMtf.m_bUseCanvas = m_bUseCanvas;
+ aMtf.m_bSVG = m_bSVG;
+
+ for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
+ {
+ const MetaActionType nType = pAction->GetType();
+
+ switch( nType )
+ {
+ case MetaActionType::PIXEL:
+ {
+ MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
+ aMtf.push_back( new MetaPixelAction( pAct->GetPoint(), pFncCol( pAct->GetColor(), pColParam ) ) );
+ }
+ break;
+
+ case MetaActionType::LINECOLOR:
+ {
+ MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction);
+
+ if( pAct->IsSetting() )
+ pAct = new MetaLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
+
+ aMtf.push_back( pAct );
+ }
+ break;
+
+ case MetaActionType::FILLCOLOR:
+ {
+ MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction);
+
+ if( pAct->IsSetting() )
+ pAct = new MetaFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
+
+ aMtf.push_back( pAct );
+ }
+ break;
+
+ case MetaActionType::TEXTCOLOR:
+ {
+ MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction);
+ aMtf.push_back( new MetaTextColorAction( pFncCol( pAct->GetColor(), pColParam ) ) );
+ }
+ break;
+
+ case MetaActionType::TEXTFILLCOLOR:
+ {
+ MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction);
+
+ if( pAct->IsSetting() )
+ pAct = new MetaTextFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
+
+ aMtf.push_back( pAct );
+ }
+ break;
+
+ case MetaActionType::TEXTLINECOLOR:
+ {
+ MetaTextLineColorAction* pAct = static_cast<MetaTextLineColorAction*>(pAction);
+
+ if( pAct->IsSetting() )
+ pAct = new MetaTextLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
+
+ aMtf.push_back( pAct );
+ }
+ break;
+
+ case MetaActionType::OVERLINECOLOR:
+ {
+ MetaOverlineColorAction* pAct = static_cast<MetaOverlineColorAction*>(pAction);
+
+ if( pAct->IsSetting() )
+ pAct = new MetaOverlineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
+
+ aMtf.push_back( pAct );
+ }
+ break;
+
+ case MetaActionType::FONT:
+ {
+ MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
+ vcl::Font aFont( pAct->GetFont() );
+
+ aFont.SetColor( pFncCol( aFont.GetColor(), pColParam ) );
+ aFont.SetFillColor( pFncCol( aFont.GetFillColor(), pColParam ) );
+ aMtf.push_back( new MetaFontAction( std::move(aFont) ) );
+ }
+ break;
+
+ case MetaActionType::WALLPAPER:
+ {
+ MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
+ Wallpaper aWall( pAct->GetWallpaper() );
+ const tools::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.push_back( new MetaWallpaperAction( rRect, std::move(aWall) ) );
+ }
+ break;
+
+ case MetaActionType::BMP:
+ case MetaActionType::BMPEX:
+ case MetaActionType::MASK:
+ {
+ OSL_FAIL( "Don't use bitmap actions of this type in metafiles!" );
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
+ aMtf.push_back( new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(),
+ pFncBmp( BitmapEx(pAct->GetBitmap()), pBmpParam ).GetBitmap() ) );
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
+ aMtf.push_back( new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
+ pAct->GetSrcPoint(), pAct->GetSrcSize(),
+ pFncBmp( BitmapEx(pAct->GetBitmap()), pBmpParam ).GetBitmap() )
+ );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
+ aMtf.push_back( new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(),
+ pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
+ );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
+ aMtf.push_back( new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
+ pAct->GetSrcPoint(), pAct->GetSrcSize(),
+ pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
+ );
+ }
+ break;
+
+ case MetaActionType::MASKSCALE:
+ {
+ MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
+ aMtf.push_back( new MetaMaskScaleAction( pAct->GetPoint(), pAct->GetSize(),
+ pAct->GetBitmap(),
+ pFncCol( pAct->GetColor(), pColParam ) )
+ );
+ }
+ break;
+
+ case MetaActionType::MASKSCALEPART:
+ {
+ MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
+ aMtf.push_back( new MetaMaskScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
+ pAct->GetSrcPoint(), pAct->GetSrcSize(),
+ pAct->GetBitmap(),
+ pFncCol( pAct->GetColor(), pColParam ) )
+ );
+ }
+ break;
+
+ case MetaActionType::GRADIENT:
+ {
+ MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
+ Gradient aGradient( pAct->GetGradient() );
+
+ aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
+ aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
+ aMtf.push_back( new MetaGradientAction( pAct->GetRect(), std::move(aGradient) ) );
+ }
+ break;
+
+ case MetaActionType::GRADIENTEX:
+ {
+ MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
+ Gradient aGradient( pAct->GetGradient() );
+
+ aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
+ aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
+ aMtf.push_back( new MetaGradientExAction( pAct->GetPolyPolygon(), std::move(aGradient) ) );
+ }
+ break;
+
+ case MetaActionType::HATCH:
+ {
+ MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
+ Hatch aHatch( pAct->GetHatch() );
+
+ aHatch.SetColor( pFncCol( aHatch.GetColor(), pColParam ) );
+ aMtf.push_back( new MetaHatchAction( pAct->GetPolyPolygon(), aHatch ) );
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
+ GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
+
+ aTransMtf.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
+ aMtf.push_back( new MetaFloatTransparentAction( aTransMtf,
+ pAct->GetPoint(), pAct->GetSize(),
+ pAct->GetGradient() )
+ );
+ }
+ break;
+
+ case MetaActionType::EPS:
+ {
+ MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
+ GDIMetaFile aSubst( pAct->GetSubstitute() );
+
+ aSubst.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
+ aMtf.push_back( new MetaEPSAction( pAct->GetPoint(), pAct->GetSize(),
+ pAct->GetLink(), aSubst )
+ );
+ }
+ break;
+
+ default:
+ {
+ aMtf.push_back( pAction );
+ }
+ break;
+ }
+ }
+
+ *this = aMtf;
+}
+
+void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
+ short nChannelRPercent, short nChannelGPercent,
+ short nChannelBPercent, double fGamma, bool bInvert, bool msoBrightness )
+{
+ // nothing to do? => return quickly
+ if( !(nLuminancePercent || nContrastPercent ||
+ nChannelRPercent || nChannelGPercent || nChannelBPercent ||
+ ( fGamma != 1.0 ) || bInvert) )
+ return;
+
+ double fM, fROff, fGOff, fBOff, fOff;
+ ImplColAdjustParam aColParam;
+ ImplBmpAdjustParam aBmpParam;
+
+ aColParam.pMapR.reset(new sal_uInt8[ 256 ]);
+ aColParam.pMapG.reset(new sal_uInt8[ 256 ]);
+ aColParam.pMapB.reset(new sal_uInt8[ 256 ]);
+
+ // calculate slope
+ if( nContrastPercent >= 0 )
+ fM = 128.0 / ( 128.0 - 1.27 * std::clamp( nContrastPercent, short(0), short(100) ) );
+ else
+ fM = ( 128.0 + 1.27 * std::clamp( nContrastPercent, short(-100), short(0) ) ) / 128.0;
+
+ if(!msoBrightness)
+ // total offset = luminance offset + contrast offset
+ fOff = std::clamp( nLuminancePercent, short(-100), short(100) ) * 2.55 + 128.0 - fM * 128.0;
+ else
+ fOff = std::clamp( nLuminancePercent, short(-100), short(100) ) * 2.55;
+
+ // 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 bool bGamma = ( fGamma != 1.0 );
+
+ // create mapping table
+ for( tools::Long nX = 0; nX < 256; nX++ )
+ {
+ if(!msoBrightness)
+ {
+ aColParam.pMapR[ nX ] = FRound(std::clamp( nX * fM + fROff, 0.0, 255.0 ));
+ aColParam.pMapG[ nX ] = FRound(std::clamp( nX * fM + fGOff, 0.0, 255.0 ));
+ aColParam.pMapB[ nX ] = FRound(std::clamp( nX * fM + fBOff, 0.0, 255.0 ));
+ }
+ else
+ {
+ aColParam.pMapR[ nX ] = FRound(std::clamp( (nX+fROff/2-128) * fM + 128 + fROff/2, 0.0, 255.0 ));
+ aColParam.pMapG[ nX ] = FRound(std::clamp( (nX+fGOff/2-128) * fM + 128 + fGOff/2, 0.0, 255.0 ));
+ aColParam.pMapB[ nX ] = FRound(std::clamp( (nX+fBOff/2-128) * fM + 128 + fBOff/2, 0.0, 255.0 ));
+ }
+ 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 );
+}
+
+void GDIMetaFile::Convert( MtfConversion eConversion )
+{
+ ImplColConvertParam aColParam;
+ ImplBmpConvertParam aBmpParam;
+
+ aColParam.eConversion = eConversion;
+ aBmpParam.eConversion = ( MtfConversion::N1BitThreshold == eConversion ) ? BmpConversion::N1BitThreshold : BmpConversion::N8BitGreys;
+
+ ImplExchangeColors( ImplColConvertFnc, &aColParam, ImplBmpConvertFnc, &aBmpParam );
+}
+
+void GDIMetaFile::ReplaceColors( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount )
+{
+ ImplColReplaceParam aColParam;
+ ImplBmpReplaceParam aBmpParam;
+
+ aColParam.pMinR.reset(new sal_uLong[ nColorCount ]);
+ aColParam.pMaxR.reset(new sal_uLong[ nColorCount ]);
+ aColParam.pMinG.reset(new sal_uLong[ nColorCount ]);
+ aColParam.pMaxG.reset(new sal_uLong[ nColorCount ]);
+ aColParam.pMinB.reset(new sal_uLong[ nColorCount ]);
+ aColParam.pMaxB.reset(new sal_uLong[ nColorCount ]);
+
+ for( sal_uLong i = 0; i < nColorCount; i++ )
+ {
+ tools::Long nVal;
+
+ nVal = pSearchColors[ i ].GetRed();
+ aColParam.pMinR[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
+ aColParam.pMaxR[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
+
+ nVal = pSearchColors[ i ].GetGreen();
+ aColParam.pMinG[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
+ aColParam.pMaxG[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
+
+ nVal = pSearchColors[ i ].GetBlue();
+ aColParam.pMinB[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
+ aColParam.pMaxB[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
+ }
+
+ aColParam.pDstCols = pReplaceColors;
+ aColParam.nCount = nColorCount;
+
+ aBmpParam.pSrcCols = pSearchColors;
+ aBmpParam.pDstCols = pReplaceColors;
+ aBmpParam.nCount = nColorCount;
+
+ ImplExchangeColors( ImplColReplaceFnc, &aColParam, ImplBmpReplaceFnc, &aBmpParam );
+};
+
+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::GetSizeBytes() const
+{
+ sal_uLong nSizeBytes = 0;
+
+ for( size_t i = 0, nObjCount = GetActionSize(); 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 MetaActionType::BMP: nSizeBytes += static_cast<MetaBmpAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
+ case MetaActionType::BMPSCALE: nSizeBytes += static_cast<MetaBmpScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
+ case MetaActionType::BMPSCALEPART: nSizeBytes += static_cast<MetaBmpScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
+
+ case MetaActionType::BMPEX: nSizeBytes += static_cast<MetaBmpExAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
+ case MetaActionType::BMPEXSCALE: nSizeBytes += static_cast<MetaBmpExScaleAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
+ case MetaActionType::BMPEXSCALEPART: nSizeBytes += static_cast<MetaBmpExScalePartAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
+
+ case MetaActionType::MASK: nSizeBytes += static_cast<MetaMaskAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
+ case MetaActionType::MASKSCALE: nSizeBytes += static_cast<MetaMaskScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
+ case MetaActionType::MASKSCALEPART: nSizeBytes += static_cast<MetaMaskScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
+
+ case MetaActionType::POLYLINE: nSizeBytes += static_cast<MetaPolyLineAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
+ case MetaActionType::POLYGON: nSizeBytes += static_cast<MetaPolygonAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
+ case MetaActionType::POLYPOLYGON:
+ {
+ const tools::PolyPolygon& rPolyPoly = static_cast<MetaPolyPolygonAction*>( pAction )->GetPolyPolygon();
+
+ for( sal_uInt16 n = 0; n < rPolyPoly.Count(); ++n )
+ nSizeBytes += ( rPolyPoly[ n ].GetSize() * sizeof( Point ) );
+ }
+ break;
+
+ case MetaActionType::TEXT: nSizeBytes += static_cast<MetaTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
+ case MetaActionType::STRETCHTEXT: nSizeBytes += static_cast<MetaStretchTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
+ case MetaActionType::TEXTRECT: nSizeBytes += static_cast<MetaTextRectAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
+ case MetaActionType::TEXTARRAY:
+ {
+ MetaTextArrayAction* pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
+
+ nSizeBytes += ( pTextArrayAction->GetText().getLength() * sizeof( sal_Unicode ) );
+
+ if( !pTextArrayAction->GetDXArray().empty() )
+ nSizeBytes += ( pTextArrayAction->GetLen() << 2 );
+ }
+ break;
+ default: break;
+ }
+ }
+
+ return nSizeBytes;
+}
+
+bool GDIMetaFile::CreateThumbnail(BitmapEx& rBitmapEx, BmpConversion eColorConversion, BmpScaleFlag nScaleFlag) const
+{
+ // initialization seems to be complicated but is used to avoid rounding errors
+ ScopedVclPtrInstance< VirtualDevice > aVDev;
+ // set Enable to tease the rendering down the code paths which use B2DPolygon and
+ // avoid integer overflows on scaling tools::Polygon, e.g. moz1545040-1.svg
+ // note: this is similar to DocumentToGraphicRenderer::renderToGraphic
+ aVDev->SetAntialiasing(AntialiasingFlags::Enable | aVDev->GetAntialiasing());
+ 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( std::abs( aBRPix.X() - aTLPix.X() ) + 1, std::abs( aBRPix.Y() - aTLPix.Y() ) + 1 );
+ sal_uInt32 nMaximumExtent = 512;
+
+ if (!rBitmapEx.IsEmpty())
+ rBitmapEx.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< tools::ULong >(aSizePix.Width()) >
+ nMaximumExtent ||
+ sal::static_int_cast< tools::ULong >(aSizePix.Height()) >
+ nMaximumExtent ) )
+ {
+ const Size aOldSizePix( aSizePix );
+ double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height();
+
+ if ( fWH <= 1.0 )
+ {
+ aSizePix.setWidth( FRound( nMaximumExtent * fWH ) );
+ aSizePix.setHeight( nMaximumExtent );
+ }
+ else
+ {
+ aSizePix.setWidth( nMaximumExtent );
+ aSizePix.setHeight( FRound( nMaximumExtent / fWH ) );
+ }
+
+ aDrawSize.setWidth( FRound( ( static_cast< double >( aDrawSize.Width() ) * aSizePix.Width() ) / aOldSizePix.Width() ) );
+ aDrawSize.setHeight( FRound( ( static_cast< double >( aDrawSize.Height() ) * aSizePix.Height() ) / aOldSizePix.Height() ) );
+ }
+
+ // draw image(s) into VDev and get resulting image
+ // do it 4x larger to be able to scale it down & get beautiful antialias
+ Size aAntialiasSize(aSizePix.Width() * 4, aSizePix.Height() * 4);
+ if (aVDev->SetOutputSizePixel(aAntialiasSize))
+ {
+ // antialias: provide 4x larger size, and then scale down the result
+ Size aAntialias(aDrawSize.Width() * 4, aDrawSize.Height() * 4);
+
+ // draw metafile into VDev
+ const_cast<GDIMetaFile *>(this)->WindStart();
+ const_cast<GDIMetaFile *>(this)->Play(*aVDev, Point(), aAntialias);
+
+ // get paint bitmap
+ BitmapEx aBitmap( aVDev->GetBitmapEx( aNullPt, aVDev->GetOutputSizePixel() ) );
+
+ // scale down the image to the desired size - use the input scaler for the scaling operation
+ aBitmap.Scale(aDrawSize, nScaleFlag);
+
+ // convert to desired bitmap color format
+ Size aSize(aBitmap.GetSizePixel());
+ if (aSize.Width() && aSize.Height())
+ aBitmap.Convert(eColorConversion);
+
+ rBitmapEx = aBitmap;
+ }
+
+ return !rBitmapEx.IsEmpty();
+}
+
+void GDIMetaFile::UseCanvas( bool _bUseCanvas )
+{
+ m_bUseCanvas = _bUseCanvas;
+}
+
+void GDIMetaFile::dumpAsXml(const char* pFileName) const
+{
+ SvFileStream aStream(pFileName ? OUString::fromUtf8(pFileName) : OUString("file:///tmp/metafile.xml"),
+ StreamMode::STD_READWRITE | StreamMode::TRUNC);
+ assert(aStream.good());
+ MetafileXmlDump aDumper;
+ aDumper.dump(*this, aStream);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */