/* -*- 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_nCurrentActionElemen
t ].get() : nullptr;
}
void GDIMetaFile::ReplaceAction( rtl::Reference<MetaAction> pAction, size_t nAction )
{
if ( nAction >= m_aList.size() )
{
return ;
}
m_aList[nAction] = std::move(pAction);
}
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() )
{
const 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 aOldOffset(rOut.GetPixelOffset());
const Size aEmptySize;
rOut.SetPixelOffset(aEmptySize);
aDrawMap.SetOrigin(rOut.PixelToLogic(rOut.LogicToPixel(rPos), aDrawMap));
rOut.SetPixelOffset(aOldOffset);
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(basegfx::fround<tools::Long >(m_aPrefSize.Width() * fScaleX));
m_aPrefSize.setHeight(basegfx::fround<tools::Long >(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 { basegfx::fround<tools::Long >(fCos * nX + fSin * nY) + rRotatePt.X() + rOffset.Width(),
basegfx::fround<tools::Long >(fCos * nY - fSin * nX) + 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(), pAct->GetLayoutContextIndex(), pAct->GetLayoutContextLen()));
}
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;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=97 H=97 G=96
¤ Dauer der Verarbeitung: 0.20 Sekunden
¤
*© Formatika GbR, Deutschland