Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/gdi/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 88 kB image not shown  

Quelle  gdimtf.cxx   Sprache: C

 
/* -*- 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;
    }
    m_aList[nAction] = std::move(pAction);
}

GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf )
{
    ifthis != &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;

    ifthis == &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&&nbsp;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






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.