bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / gdi / gdimtf.cxx
blobdea25de63719aa9361635ecd6d750f8cdbe89de1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <cstdlib>
21 #include <memory>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <tools/diagnose_ex.h>
25 #include <tools/helpers.hxx>
26 #include <tools/stream.hxx>
27 #include <tools/vcompat.hxx>
28 #include <tools/fract.hxx>
29 #include <vcl/BitmapPalette.hxx>
30 #include <vcl/metaact.hxx>
31 #include <vcl/outdev.hxx>
32 #include <vcl/window.hxx>
33 #include <vcl/virdev.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/gdimtf.hxx>
36 #include <vcl/graphictools.hxx>
37 #include <basegfx/polygon/b2dpolygon.hxx>
38 #include <vcl/canvastools.hxx>
39 #include <vcl/mtfxmldump.hxx>
41 #include <svmconverter.hxx>
42 #include <TypeSerializer.hxx>
44 #include <com/sun/star/beans/XFastPropertySet.hpp>
45 #include <com/sun/star/rendering/MtfRenderer.hpp>
46 #include <com/sun/star/rendering/XBitmapCanvas.hpp>
47 #include <com/sun/star/rendering/XCanvas.hpp>
48 #include <comphelper/processfactory.hxx>
50 using namespace com::sun::star;
52 #define GAMMA( _def_cVal, _def_InvGamma ) (static_cast<sal_uInt8>(MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0,255)))
54 struct ImplColAdjustParam
56 std::unique_ptr<sal_uInt8[]> pMapR;
57 std::unique_ptr<sal_uInt8[]> pMapG;
58 std::unique_ptr<sal_uInt8[]> pMapB;
61 struct ImplBmpAdjustParam
63 short nLuminancePercent;
64 short nContrastPercent;
65 short nChannelRPercent;
66 short nChannelGPercent;
67 short nChannelBPercent;
68 double fGamma;
69 bool bInvert;
72 struct ImplColConvertParam
74 MtfConversion eConversion;
77 struct ImplBmpConvertParam
79 BmpConversion eConversion;
82 struct ImplColMonoParam
84 Color aColor;
87 struct ImplBmpMonoParam
89 Color aColor;
92 struct ImplColReplaceParam
94 std::unique_ptr<sal_uLong[]> pMinR;
95 std::unique_ptr<sal_uLong[]> pMaxR;
96 std::unique_ptr<sal_uLong[]> pMinG;
97 std::unique_ptr<sal_uLong[]> pMaxG;
98 std::unique_ptr<sal_uLong[]> pMinB;
99 std::unique_ptr<sal_uLong[]> pMaxB;
100 const Color * pDstCols;
101 sal_uLong nCount;
104 struct ImplBmpReplaceParam
106 const Color* pSrcCols;
107 const Color* pDstCols;
108 sal_uLong nCount;
111 GDIMetaFile::GDIMetaFile() :
112 m_nCurrentActionElement( 0 ),
113 m_aPrefSize ( 1, 1 ),
114 m_pPrev ( nullptr ),
115 m_pNext ( nullptr ),
116 m_pOutDev ( nullptr ),
117 m_bPause ( false ),
118 m_bRecord ( false ),
119 m_bUseCanvas ( false )
123 GDIMetaFile::GDIMetaFile( const GDIMetaFile& rMtf ) :
124 m_nCurrentActionElement( rMtf.m_nCurrentActionElement ),
125 m_aPrefMapMode ( rMtf.m_aPrefMapMode ),
126 m_aPrefSize ( rMtf.m_aPrefSize ),
127 m_pPrev ( rMtf.m_pPrev ),
128 m_pNext ( rMtf.m_pNext ),
129 m_pOutDev ( nullptr ),
130 m_bPause ( false ),
131 m_bRecord ( false ),
132 m_bUseCanvas ( rMtf.m_bUseCanvas )
134 for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
136 m_aList.push_back( rMtf.GetAction( i ) );
139 if( rMtf.m_bRecord )
141 Record( rMtf.m_pOutDev );
143 if ( rMtf.m_bPause )
144 Pause( true );
148 GDIMetaFile::~GDIMetaFile()
150 Clear();
153 size_t GDIMetaFile::GetActionSize() const
155 return m_aList.size();
158 MetaAction* GDIMetaFile::GetAction( size_t nAction ) const
160 return (nAction < m_aList.size()) ? m_aList[ nAction ].get() : nullptr;
163 MetaAction* GDIMetaFile::FirstAction()
165 m_nCurrentActionElement = 0;
166 return m_aList.empty() ? nullptr : m_aList[ 0 ].get();
169 MetaAction* GDIMetaFile::NextAction()
171 return ( m_nCurrentActionElement + 1 < m_aList.size() ) ? m_aList[ ++m_nCurrentActionElement ].get() : nullptr;
174 void GDIMetaFile::ReplaceAction( rtl::Reference<MetaAction> pAction, size_t nAction )
176 if ( nAction >= m_aList.size() )
178 return;
180 //fdo#39995 This doesn't increment the incoming action ref-count nor does it
181 //decrement the outgoing action ref-count
182 std::swap(pAction, m_aList[nAction]);
185 GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf )
187 if( this != &rMtf )
189 Clear();
191 // Increment RefCount of MetaActions
192 for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
194 m_aList.push_back( rMtf.GetAction( i ) );
197 m_aPrefMapMode = rMtf.m_aPrefMapMode;
198 m_aPrefSize = rMtf.m_aPrefSize;
199 m_pPrev = rMtf.m_pPrev;
200 m_pNext = rMtf.m_pNext;
201 m_pOutDev = nullptr;
202 m_bPause = false;
203 m_bRecord = false;
204 m_bUseCanvas = rMtf.m_bUseCanvas;
206 if( rMtf.m_bRecord )
208 Record( rMtf.m_pOutDev );
210 if( rMtf.m_bPause )
211 Pause( true );
215 return *this;
218 bool GDIMetaFile::operator==( const GDIMetaFile& rMtf ) const
220 const size_t nObjCount = m_aList.size();
221 bool bRet = false;
223 if( this == &rMtf )
224 bRet = true;
225 else if( rMtf.GetActionSize() == nObjCount &&
226 rMtf.GetPrefSize() == m_aPrefSize &&
227 rMtf.GetPrefMapMode() == m_aPrefMapMode )
229 bRet = true;
231 for( size_t n = 0; n < nObjCount; n++ )
233 if( m_aList[ n ] != rMtf.GetAction( n ) )
235 bRet = false;
236 break;
241 return bRet;
244 void GDIMetaFile::Clear()
246 if( m_bRecord )
247 Stop();
249 m_aList.clear();
252 void GDIMetaFile::Linker( OutputDevice* pOut, bool bLink )
254 if( bLink )
256 m_pNext = nullptr;
257 m_pPrev = pOut->GetConnectMetaFile();
258 pOut->SetConnectMetaFile( this );
260 if( m_pPrev )
261 m_pPrev->m_pNext = this;
263 else
265 if( m_pNext )
267 m_pNext->m_pPrev = m_pPrev;
269 if( m_pPrev )
270 m_pPrev->m_pNext = m_pNext;
272 else
274 if( m_pPrev )
275 m_pPrev->m_pNext = nullptr;
277 pOut->SetConnectMetaFile( m_pPrev );
280 m_pPrev = nullptr;
281 m_pNext = nullptr;
285 void GDIMetaFile::Record( OutputDevice* pOut )
287 if( m_bRecord )
288 Stop();
290 m_nCurrentActionElement = m_aList.empty() ? 0 : (m_aList.size() - 1);
291 m_pOutDev = pOut;
292 m_bRecord = true;
293 Linker( pOut, true );
296 void GDIMetaFile::Play( GDIMetaFile& rMtf )
298 if ( !m_bRecord && !rMtf.m_bRecord )
300 MetaAction* pAction = GetCurAction();
301 const size_t nObjCount = m_aList.size();
303 rMtf.UseCanvas( rMtf.GetUseCanvas() || m_bUseCanvas );
305 for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nObjCount; nCurPos++ )
307 if( pAction )
309 rMtf.AddAction( pAction );
312 pAction = NextAction();
317 void GDIMetaFile::Play( OutputDevice* pOut, size_t nPos )
319 if( !m_bRecord )
321 MetaAction* pAction = GetCurAction();
322 const size_t nObjCount = m_aList.size();
323 size_t nSyncCount = ( pOut->GetOutDevType() == OUTDEV_WINDOW ) ? 0x000000ff : 0xffffffff;
325 if( nPos > nObjCount )
326 nPos = nObjCount;
328 // #i23407# Set backwards-compatible text language and layout mode
329 // This is necessary, since old metafiles don't even know of these
330 // recent add-ons. Newer metafiles must of course explicitly set
331 // those states.
332 pOut->Push( PushFlags::TEXTLAYOUTMODE|PushFlags::TEXTLANGUAGE );
333 pOut->SetLayoutMode( ComplexTextLayoutFlags::Default );
334 pOut->SetDigitLanguage( LANGUAGE_SYSTEM );
336 SAL_INFO( "vcl.gdi", "GDIMetaFile::Play on device of size: " << pOut->GetOutputSizePixel().Width() << " " << pOut->GetOutputSizePixel().Height());
338 if( !ImplPlayWithRenderer( pOut, Point(0,0), pOut->GetOutputSize() ) ) {
339 size_t i = 0;
340 for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nPos; nCurPos++ )
342 if( pAction )
344 pAction->Execute( pOut );
346 // flush output from time to time
347 if( i++ > nSyncCount )
349 static_cast<vcl::Window*>( pOut )->Flush();
350 i = 0;
354 pAction = NextAction();
357 pOut->Pop();
361 bool GDIMetaFile::ImplPlayWithRenderer( OutputDevice* pOut, const Point& rPos, Size rLogicDestSize )
363 if (!m_bUseCanvas)
364 return false;
366 Size rDestSize( pOut->LogicToPixel( rLogicDestSize ) );
368 const vcl::Window* win = dynamic_cast <vcl::Window*> ( pOut );
370 if (!win)
371 win = Application::GetActiveTopWindow();
372 if (!win)
373 win = Application::GetFirstTopLevelWindow();
375 if (!win)
376 return false;
380 uno::Reference<rendering::XCanvas> xCanvas = win->GetCanvas ();
382 if (!xCanvas.is())
383 return false;
385 Size aSize (rDestSize.Width () + 1, rDestSize.Height () + 1);
386 uno::Reference<rendering::XBitmap> xBitmap = xCanvas->getDevice ()->createCompatibleAlphaBitmap (vcl::unotools::integerSize2DFromSize( aSize));
387 if( xBitmap.is () )
389 uno::Reference< rendering::XBitmapCanvas > xBitmapCanvas( xBitmap, uno::UNO_QUERY );
390 if( xBitmapCanvas.is() )
392 uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
393 uno::Reference< rendering::XMtfRenderer > xMtfRenderer = rendering::MtfRenderer::createWithBitmapCanvas( xContext, xBitmapCanvas );
395 xBitmapCanvas->clear();
396 uno::Reference< beans::XFastPropertySet > xMtfFastPropertySet( xMtfRenderer, uno::UNO_QUERY );
397 if( xMtfFastPropertySet.is() )
398 // set this metafile to the renderer to
399 // speedup things (instead of copying data to
400 // sequence of bytes passed to renderer)
401 xMtfFastPropertySet->setFastPropertyValue( 0, uno::Any( reinterpret_cast<sal_Int64>( this ) ) );
403 xMtfRenderer->draw( rDestSize.Width(), rDestSize.Height() );
405 BitmapEx aBitmapEx;
406 if( aBitmapEx.Create( xBitmapCanvas, aSize ) )
408 if (pOut->GetMapMode().GetMapUnit() == MapUnit::MapPixel)
409 pOut->DrawBitmapEx( rPos, aBitmapEx );
410 else
411 pOut->DrawBitmapEx( rPos, rLogicDestSize, aBitmapEx );
412 return true;
417 catch (const uno::RuntimeException& )
419 throw; // runtime errors are fatal
421 catch (const uno::Exception&)
423 // ignore errors, no way of reporting them here
424 TOOLS_WARN_EXCEPTION("vcl.gdi", "GDIMetaFile::ImplPlayWithRenderer");
427 return false;
430 void GDIMetaFile::Play( OutputDevice* pOut, const Point& rPos,
431 const Size& rSize )
433 vcl::Region aDrawClipRegion;
434 MapMode aDrawMap( GetPrefMapMode() );
435 Size aDestSize( pOut->LogicToPixel( rSize ) );
437 if( !aDestSize.Width() || !aDestSize.Height() )
438 return;
440 GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
442 if( ImplPlayWithRenderer( pOut, rPos, rSize ) )
443 return;
445 Size aTmpPrefSize( pOut->LogicToPixel( GetPrefSize(), aDrawMap ) );
447 if( !aTmpPrefSize.Width() )
448 aTmpPrefSize.setWidth( aDestSize.Width() );
450 if( !aTmpPrefSize.Height() )
451 aTmpPrefSize.setHeight( aDestSize.Height() );
453 Fraction aScaleX( aDestSize.Width(), aTmpPrefSize.Width() );
454 Fraction aScaleY( aDestSize.Height(), aTmpPrefSize.Height() );
456 aScaleX *= aDrawMap.GetScaleX(); aDrawMap.SetScaleX( aScaleX );
457 aScaleY *= aDrawMap.GetScaleY(); aDrawMap.SetScaleY( aScaleY );
459 // #i47260# Convert logical output position to offset within
460 // the metafile's mapmode. Therefore, disable pixel offset on
461 // outdev, it's inverse mnOutOffLogicX/Y is calculated for a
462 // different mapmode (the one currently set on pOut, that is)
463 // - thus, aDrawMap's origin would generally be wrong. And
464 // even _if_ aDrawMap is similar to pOutDev's current mapmode,
465 // it's _still_ undesirable to have pixel offset unequal zero,
466 // because one would still get round-off errors (the
467 // round-trip error for LogicToPixel( PixelToLogic() ) was the
468 // reason for having pixel offset in the first place).
469 const Size& rOldOffset( pOut->GetPixelOffset() );
470 const Size aEmptySize;
471 pOut->SetPixelOffset( aEmptySize );
472 aDrawMap.SetOrigin( pOut->PixelToLogic( pOut->LogicToPixel( rPos ), aDrawMap ) );
473 pOut->SetPixelOffset( rOldOffset );
475 pOut->Push();
477 if ( pMtf && pMtf->IsRecord() && ( pOut->GetOutDevType() != OUTDEV_PRINTER ) )
478 pOut->SetRelativeMapMode( aDrawMap );
479 else
480 pOut->SetMapMode( aDrawMap );
482 // #i23407# Set backwards-compatible text language and layout mode
483 // This is necessary, since old metafiles don't even know of these
484 // recent add-ons. Newer metafiles must of course explicitly set
485 // those states.
486 pOut->SetLayoutMode( ComplexTextLayoutFlags::Default );
487 pOut->SetDigitLanguage( LANGUAGE_SYSTEM );
489 Play( pOut );
491 pOut->Pop();
495 void GDIMetaFile::Pause( bool _bPause )
497 if( m_bRecord )
499 if( _bPause )
501 if( !m_bPause )
502 Linker( m_pOutDev, false );
504 else
506 if( m_bPause )
507 Linker( m_pOutDev, true );
510 m_bPause = _bPause;
514 void GDIMetaFile::Stop()
516 if( m_bRecord )
518 m_bRecord = false;
520 if( !m_bPause )
521 Linker( m_pOutDev, false );
522 else
523 m_bPause = false;
527 void GDIMetaFile::WindStart()
529 if( !m_bRecord )
530 m_nCurrentActionElement = 0;
533 void GDIMetaFile::WindPrev()
535 if( !m_bRecord )
536 if ( m_nCurrentActionElement > 0 )
537 --m_nCurrentActionElement;
540 void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction)
542 m_aList.push_back( pAction );
544 if( m_pPrev )
546 m_pPrev->AddAction( pAction );
550 void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction, size_t nPos)
552 if ( nPos < m_aList.size() )
554 m_aList.insert( m_aList.begin() + nPos, pAction );
556 else
558 m_aList.push_back( pAction );
561 if( m_pPrev )
563 m_pPrev->AddAction( pAction, nPos );
567 void GDIMetaFile::push_back(const rtl::Reference<MetaAction>& pAction)
569 m_aList.push_back( pAction );
572 void GDIMetaFile::Mirror( BmpMirrorFlags nMirrorFlags )
574 const Size aOldPrefSize( GetPrefSize() );
575 long nMoveX, nMoveY;
576 double fScaleX, fScaleY;
578 if( nMirrorFlags & BmpMirrorFlags::Horizontal )
580 nMoveX = std::abs( aOldPrefSize.Width() ) - 1;
581 fScaleX = -1.0;
583 else
585 nMoveX = 0;
586 fScaleX = 1.0;
589 if( nMirrorFlags & BmpMirrorFlags::Vertical )
591 nMoveY = std::abs( aOldPrefSize.Height() ) - 1;
592 fScaleY = -1.0;
594 else
596 nMoveY = 0;
597 fScaleY = 1.0;
600 if( ( fScaleX != 1.0 ) || ( fScaleY != 1.0 ) )
602 Scale( fScaleX, fScaleY );
603 Move( nMoveX, nMoveY );
604 SetPrefSize( aOldPrefSize );
608 void GDIMetaFile::Move( long nX, long nY )
610 const Size aBaseOffset( nX, nY );
611 Size aOffset( aBaseOffset );
612 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
614 aMapVDev->EnableOutput( false );
615 aMapVDev->SetMapMode( GetPrefMapMode() );
617 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
619 const MetaActionType nType = pAct->GetType();
620 MetaAction* pModAct;
622 if( pAct->GetRefCount() > 1 )
624 m_aList[ m_nCurrentActionElement ] = pAct->Clone();
625 pModAct = m_aList[ m_nCurrentActionElement ].get();
627 else
628 pModAct = pAct;
630 if( ( MetaActionType::MAPMODE == nType ) ||
631 ( MetaActionType::PUSH == nType ) ||
632 ( MetaActionType::POP == nType ) )
634 pModAct->Execute( aMapVDev.get() );
635 aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
638 pModAct->Move( aOffset.Width(), aOffset.Height() );
642 void GDIMetaFile::Move( long nX, long nY, long nDPIX, long nDPIY )
644 const Size aBaseOffset( nX, nY );
645 Size aOffset( aBaseOffset );
646 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
648 aMapVDev->EnableOutput( false );
649 aMapVDev->SetReferenceDevice( nDPIX, nDPIY );
650 aMapVDev->SetMapMode( GetPrefMapMode() );
652 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
654 const MetaActionType nType = pAct->GetType();
655 MetaAction* pModAct;
657 if( pAct->GetRefCount() > 1 )
659 m_aList[ m_nCurrentActionElement ] = pAct->Clone();
660 pModAct = m_aList[ m_nCurrentActionElement ].get();
662 else
663 pModAct = pAct;
665 if( ( MetaActionType::MAPMODE == nType ) ||
666 ( MetaActionType::PUSH == nType ) ||
667 ( MetaActionType::POP == nType ) )
669 pModAct->Execute( aMapVDev.get() );
670 if( aMapVDev->GetMapMode().GetMapUnit() == MapUnit::MapPixel )
672 aOffset = aMapVDev->LogicToPixel( aBaseOffset, GetPrefMapMode() );
673 MapMode aMap( aMapVDev->GetMapMode() );
674 aOffset.setWidth( static_cast<long>(aOffset.Width() * static_cast<double>(aMap.GetScaleX())) );
675 aOffset.setHeight( static_cast<long>(aOffset.Height() * static_cast<double>(aMap.GetScaleY())) );
677 else
678 aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
681 pModAct->Move( aOffset.Width(), aOffset.Height() );
685 void GDIMetaFile::Scale( double fScaleX, double fScaleY )
687 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
689 MetaAction* pModAct;
691 if( pAct->GetRefCount() > 1 )
693 m_aList[ m_nCurrentActionElement ] = pAct->Clone();
694 pModAct = m_aList[ m_nCurrentActionElement ].get();
696 else
697 pModAct = pAct;
699 pModAct->Scale( fScaleX, fScaleY );
702 m_aPrefSize.setWidth( FRound( m_aPrefSize.Width() * fScaleX ) );
703 m_aPrefSize.setHeight( FRound( m_aPrefSize.Height() * fScaleY ) );
706 void GDIMetaFile::Scale( const Fraction& rScaleX, const Fraction& rScaleY )
708 Scale( static_cast<double>(rScaleX), static_cast<double>(rScaleY) );
711 void GDIMetaFile::Clip( const tools::Rectangle& i_rClipRect )
713 tools::Rectangle aCurRect( i_rClipRect );
714 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
716 aMapVDev->EnableOutput( false );
717 aMapVDev->SetMapMode( GetPrefMapMode() );
719 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
721 const MetaActionType nType = pAct->GetType();
723 if( ( MetaActionType::MAPMODE == nType ) ||
724 ( MetaActionType::PUSH == nType ) ||
725 ( MetaActionType::POP == nType ) )
727 pAct->Execute( aMapVDev.get() );
728 aCurRect = OutputDevice::LogicToLogic( i_rClipRect, GetPrefMapMode(), aMapVDev->GetMapMode() );
730 else if( nType == MetaActionType::CLIPREGION )
732 MetaClipRegionAction* pOldAct = static_cast<MetaClipRegionAction*>(pAct);
733 vcl::Region aNewReg( aCurRect );
734 if( pOldAct->IsClipping() )
735 aNewReg.Intersect( pOldAct->GetRegion() );
736 MetaClipRegionAction* pNewAct = new MetaClipRegionAction( aNewReg, true );
737 m_aList[ m_nCurrentActionElement ] = pNewAct;
742 Point GDIMetaFile::ImplGetRotatedPoint( const Point& rPt, const Point& rRotatePt,
743 const Size& rOffset, double fSin, double fCos )
745 const long nX = rPt.X() - rRotatePt.X();
746 const long nY = rPt.Y() - rRotatePt.Y();
748 return Point( FRound( fCos * nX + fSin * nY ) + rRotatePt.X() + rOffset.Width(),
749 -FRound( fSin * nX - fCos * nY ) + rRotatePt.Y() + rOffset.Height() );
752 tools::Polygon GDIMetaFile::ImplGetRotatedPolygon( const tools::Polygon& rPoly, const Point& rRotatePt,
753 const Size& rOffset, double fSin, double fCos )
755 tools::Polygon aRet( rPoly );
757 aRet.Rotate( rRotatePt, fSin, fCos );
758 aRet.Move( rOffset.Width(), rOffset.Height() );
760 return aRet;
763 tools::PolyPolygon GDIMetaFile::ImplGetRotatedPolyPolygon( const tools::PolyPolygon& rPolyPoly, const Point& rRotatePt,
764 const Size& rOffset, double fSin, double fCos )
766 tools::PolyPolygon aRet( rPolyPoly );
768 aRet.Rotate( rRotatePt, fSin, fCos );
769 aRet.Move( rOffset.Width(), rOffset.Height() );
771 return aRet;
774 void GDIMetaFile::ImplAddGradientEx( GDIMetaFile& rMtf,
775 const OutputDevice& rMapDev,
776 const tools::PolyPolygon& rPolyPoly,
777 const Gradient& rGrad )
779 // Generate comment, GradientEx and Gradient actions (within DrawGradient)
780 ScopedVclPtrInstance< VirtualDevice > aVDev(rMapDev, DeviceFormat::DEFAULT);
781 aVDev->EnableOutput( false );
782 GDIMetaFile aGradMtf;
784 aGradMtf.Record( aVDev.get() );
785 aVDev->DrawGradient( rPolyPoly, rGrad );
786 aGradMtf.Stop();
788 size_t i, nAct( aGradMtf.GetActionSize() );
789 for( i=0; i < nAct; ++i )
791 MetaAction* pMetaAct = aGradMtf.GetAction( i );
792 rMtf.AddAction( pMetaAct );
796 void GDIMetaFile::Rotate( long nAngle10 )
798 nAngle10 %= 3600;
799 nAngle10 = ( nAngle10 < 0 ) ? ( 3599 + nAngle10 ) : nAngle10;
801 if( !nAngle10 )
802 return;
804 GDIMetaFile aMtf;
805 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
806 const double fAngle = F_PI1800 * nAngle10;
807 const double fSin = sin( fAngle );
808 const double fCos = cos( fAngle );
809 tools::Rectangle aRect( Point(), GetPrefSize() );
810 tools::Polygon aPoly( aRect );
812 aPoly.Rotate( Point(), fSin, fCos );
814 aMapVDev->EnableOutput( false );
815 aMapVDev->SetMapMode( GetPrefMapMode() );
817 const tools::Rectangle aNewBound( aPoly.GetBoundRect() );
819 const Point aOrigin( GetPrefMapMode().GetOrigin().X(), GetPrefMapMode().GetOrigin().Y() );
820 const Size aOffset( -aNewBound.Left(), -aNewBound.Top() );
822 Point aRotAnchor( aOrigin );
823 Size aRotOffset( aOffset );
825 for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
827 const MetaActionType nActionType = pAction->GetType();
829 switch( nActionType )
831 case MetaActionType::PIXEL:
833 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
834 aMtf.AddAction( new MetaPixelAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
835 pAct->GetColor() ) );
837 break;
839 case MetaActionType::POINT:
841 MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
842 aMtf.AddAction( new MetaPointAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
844 break;
846 case MetaActionType::LINE:
848 MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
849 aMtf.AddAction( new MetaLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
850 ImplGetRotatedPoint( pAct->GetEndPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
851 pAct->GetLineInfo() ) );
853 break;
855 case MetaActionType::RECT:
857 MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
858 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
860 break;
862 case MetaActionType::ROUNDRECT:
864 MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
865 const tools::Polygon aRoundRectPoly( pAct->GetRect(), pAct->GetHorzRound(), pAct->GetVertRound() );
867 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aRoundRectPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
869 break;
871 case MetaActionType::ELLIPSE:
873 MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
874 const tools::Polygon aEllipsePoly( pAct->GetRect().Center(), pAct->GetRect().GetWidth() >> 1, pAct->GetRect().GetHeight() >> 1 );
876 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aEllipsePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
878 break;
880 case MetaActionType::ARC:
882 MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
883 const tools::Polygon aArcPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Arc );
885 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aArcPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
887 break;
889 case MetaActionType::PIE:
891 MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
892 const tools::Polygon aPiePoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Pie );
894 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aPiePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
896 break;
898 case MetaActionType::CHORD:
900 MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
901 const tools::Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Chord );
903 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aChordPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
905 break;
907 case MetaActionType::POLYLINE:
909 MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
910 aMtf.AddAction( new MetaPolyLineAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->GetLineInfo() ) );
912 break;
914 case MetaActionType::POLYGON:
916 MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
917 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
919 break;
921 case MetaActionType::POLYPOLYGON:
923 MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
924 aMtf.AddAction( new MetaPolyPolygonAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
926 break;
928 case MetaActionType::TEXT:
930 MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
931 aMtf.AddAction( new MetaTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
932 pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
934 break;
936 case MetaActionType::TEXTARRAY:
938 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
939 aMtf.AddAction( new MetaTextArrayAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
940 pAct->GetText(), pAct->GetDXArray(), pAct->GetIndex(), pAct->GetLen() ) );
942 break;
944 case MetaActionType::STRETCHTEXT:
946 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
947 aMtf.AddAction( new MetaStretchTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
948 pAct->GetWidth(), pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
950 break;
952 case MetaActionType::TEXTLINE:
954 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
955 aMtf.AddAction( new MetaTextLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
956 pAct->GetWidth(), pAct->GetStrikeout(), pAct->GetUnderline(), pAct->GetOverline() ) );
958 break;
960 case MetaActionType::BMPSCALE:
962 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
963 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
964 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
965 BitmapEx aBmpEx( pAct->GetBitmap() );
967 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
968 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(),
969 aBmpEx ) );
971 break;
973 case MetaActionType::BMPSCALEPART:
975 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
976 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
977 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
978 BitmapEx aBmpEx( pAct->GetBitmap() );
980 aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
981 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
983 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
985 break;
987 case MetaActionType::BMPEXSCALE:
989 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
990 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
991 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
992 BitmapEx aBmpEx( pAct->GetBitmapEx() );
994 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
996 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
998 break;
1000 case MetaActionType::BMPEXSCALEPART:
1002 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1003 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1004 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1005 BitmapEx aBmpEx( pAct->GetBitmapEx() );
1007 aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
1008 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
1010 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
1012 break;
1014 case MetaActionType::GRADIENT:
1016 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1018 ImplAddGradientEx( aMtf, *aMapVDev,
1019 ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ),
1020 pAct->GetGradient() );
1022 break;
1024 case MetaActionType::GRADIENTEX:
1026 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1027 aMtf.AddAction( new MetaGradientExAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1028 pAct->GetGradient() ) );
1030 break;
1032 // Handle gradientex comment block correctly
1033 case MetaActionType::COMMENT:
1035 MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction);
1036 if( pCommentAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1038 int nBeginComments( 1 );
1039 pAction = NextAction();
1041 // skip everything, except gradientex action
1042 while( pAction )
1044 const MetaActionType nType = pAction->GetType();
1046 if( MetaActionType::GRADIENTEX == nType )
1048 // Add rotated gradientex
1049 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1050 ImplAddGradientEx( aMtf, *aMapVDev,
1051 ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1052 pAct->GetGradient() );
1054 else if( MetaActionType::COMMENT == nType)
1056 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pAction);
1057 if( pAct->GetComment() == "XGRAD_SEQ_END" )
1059 // handle nested blocks
1060 --nBeginComments;
1062 // gradientex comment block: end reached, done.
1063 if( !nBeginComments )
1064 break;
1066 else if( pAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1068 // handle nested blocks
1069 ++nBeginComments;
1074 pAction =NextAction();
1077 else
1079 bool bPathStroke = (pCommentAct->GetComment() == "XPATHSTROKE_SEQ_BEGIN");
1080 if ( bPathStroke || pCommentAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1082 if ( pCommentAct->GetDataSize() )
1084 SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pCommentAct->GetData()), pCommentAct->GetDataSize(), StreamMode::READ );
1085 SvMemoryStream aDest;
1086 if ( bPathStroke )
1088 SvtGraphicStroke aStroke;
1089 ReadSvtGraphicStroke( aMemStm, aStroke );
1090 tools::Polygon aPath;
1091 aStroke.getPath( aPath );
1092 aStroke.setPath( ImplGetRotatedPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1093 WriteSvtGraphicStroke( aDest, aStroke );
1094 aMtf.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN", 0,
1095 static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1097 else
1099 SvtGraphicFill aFill;
1100 ReadSvtGraphicFill( aMemStm, aFill );
1101 tools::PolyPolygon aPath;
1102 aFill.getPath( aPath );
1103 aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1104 WriteSvtGraphicFill( aDest, aFill );
1105 aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0,
1106 static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1110 else if ( pCommentAct->GetComment() == "XPATHSTROKE_SEQ_END"
1111 || pCommentAct->GetComment() == "XPATHFILL_SEQ_END" )
1113 pAction->Execute( aMapVDev.get() );
1114 aMtf.AddAction( pAction );
1118 break;
1120 case MetaActionType::HATCH:
1122 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
1123 Hatch aHatch( pAct->GetHatch() );
1125 aHatch.SetAngle( aHatch.GetAngle() + static_cast<sal_uInt16>(nAngle10) );
1126 aMtf.AddAction( new MetaHatchAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1127 aHatch ) );
1129 break;
1131 case MetaActionType::Transparent:
1133 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1134 aMtf.AddAction( new MetaTransparentAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1135 pAct->GetTransparence() ) );
1137 break;
1139 case MetaActionType::FLOATTRANSPARENT:
1141 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1142 GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
1143 tools::Polygon aMtfPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1144 tools::Rectangle aMtfRect( aMtfPoly.GetBoundRect() );
1146 aTransMtf.Rotate( nAngle10 );
1147 aMtf.AddAction( new MetaFloatTransparentAction( aTransMtf, aMtfRect.TopLeft(), aMtfRect.GetSize(),
1148 pAct->GetGradient() ) );
1150 break;
1152 case MetaActionType::EPS:
1154 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
1155 GDIMetaFile aEPSMtf( pAct->GetSubstitute() );
1156 tools::Polygon aEPSPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1157 tools::Rectangle aEPSRect( aEPSPoly.GetBoundRect() );
1159 aEPSMtf.Rotate( nAngle10 );
1160 aMtf.AddAction( new MetaEPSAction( aEPSRect.TopLeft(), aEPSRect.GetSize(),
1161 pAct->GetLink(), aEPSMtf ) );
1163 break;
1165 case MetaActionType::CLIPREGION:
1167 MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1169 if( pAct->IsClipping() && pAct->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1170 aMtf.AddAction( new MetaClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( pAct->GetRegion().GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ), true ) );
1171 else
1173 aMtf.AddAction( pAction );
1176 break;
1178 case MetaActionType::ISECTRECTCLIPREGION:
1180 MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1181 aMtf.AddAction( new MetaISectRegionClipRegionAction(vcl::Region(
1182 ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor,
1183 aRotOffset, fSin, fCos )) ) );
1185 break;
1187 case MetaActionType::ISECTREGIONCLIPREGION:
1189 MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1190 const vcl::Region& rRegion = pAct->GetRegion();
1192 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() )
1193 aMtf.AddAction( new MetaISectRegionClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( rRegion.GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ) );
1194 else
1196 aMtf.AddAction( pAction );
1199 break;
1201 case MetaActionType::REFPOINT:
1203 MetaRefPointAction* pAct = static_cast<MetaRefPointAction*>(pAction);
1204 aMtf.AddAction( new MetaRefPointAction( ImplGetRotatedPoint( pAct->GetRefPoint(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->IsSetting() ) );
1206 break;
1208 case MetaActionType::FONT:
1210 MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1211 vcl::Font aFont( pAct->GetFont() );
1213 aFont.SetOrientation( aFont.GetOrientation() + static_cast<sal_uInt16>(nAngle10) );
1214 aMtf.AddAction( new MetaFontAction( aFont ) );
1216 break;
1218 case MetaActionType::BMP:
1219 case MetaActionType::BMPEX:
1220 case MetaActionType::MASK:
1221 case MetaActionType::MASKSCALE:
1222 case MetaActionType::MASKSCALEPART:
1223 case MetaActionType::WALLPAPER:
1224 case MetaActionType::TEXTRECT:
1225 case MetaActionType::MOVECLIPREGION:
1227 OSL_FAIL( "GDIMetaFile::Rotate(): unsupported action" );
1229 break;
1231 default:
1233 pAction->Execute( aMapVDev.get() );
1234 aMtf.AddAction( pAction );
1236 // update rotation point and offset, if necessary
1237 if( ( MetaActionType::MAPMODE == nActionType ) ||
1238 ( MetaActionType::PUSH == nActionType ) ||
1239 ( MetaActionType::POP == nActionType ) )
1241 aRotAnchor = OutputDevice::LogicToLogic( aOrigin, m_aPrefMapMode, aMapVDev->GetMapMode() );
1242 aRotOffset = OutputDevice::LogicToLogic( aOffset, m_aPrefMapMode, aMapVDev->GetMapMode() );
1245 break;
1249 aMtf.m_aPrefMapMode = m_aPrefMapMode;
1250 aMtf.m_aPrefSize = aNewBound.GetSize();
1252 *this = aMtf;
1256 static void ImplActionBounds( tools::Rectangle& o_rOutBounds,
1257 const tools::Rectangle& i_rInBounds,
1258 const std::vector<tools::Rectangle>& i_rClipStack,
1259 tools::Rectangle* o_pHairline )
1261 tools::Rectangle aBounds( i_rInBounds );
1262 if( ! i_rInBounds.IsEmpty() && ! i_rClipStack.empty() && ! i_rClipStack.back().IsEmpty() )
1263 aBounds.Intersection( i_rClipStack.back() );
1264 if( ! aBounds.IsEmpty() )
1266 if( ! o_rOutBounds.IsEmpty() )
1267 o_rOutBounds.Union( aBounds );
1268 else
1269 o_rOutBounds = aBounds;
1271 if(o_pHairline)
1273 if( ! o_pHairline->IsEmpty() )
1274 o_pHairline->Union( aBounds );
1275 else
1276 *o_pHairline = aBounds;
1281 tools::Rectangle GDIMetaFile::GetBoundRect( OutputDevice& i_rReference, tools::Rectangle* pHairline ) const
1283 GDIMetaFile aMtf;
1284 ScopedVclPtrInstance< VirtualDevice > aMapVDev( i_rReference );
1286 aMapVDev->EnableOutput( false );
1287 aMapVDev->SetMapMode( GetPrefMapMode() );
1289 std::vector<tools::Rectangle> aClipStack( 1, tools::Rectangle() );
1290 std::vector<PushFlags> aPushFlagStack;
1292 tools::Rectangle aBound;
1294 if(pHairline)
1295 *pHairline = tools::Rectangle();
1297 const sal_uLong nCount(GetActionSize());
1299 for(sal_uLong a(0); a < nCount; a++)
1301 MetaAction* pAction = GetAction(a);
1302 const MetaActionType nActionType = pAction->GetType();
1303 tools::Rectangle* pUseHairline = (pHairline && aMapVDev->IsLineColor()) ? pHairline : nullptr;
1305 switch( nActionType )
1307 case MetaActionType::PIXEL:
1309 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1310 ImplActionBounds( aBound,
1311 tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1312 aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1313 aClipStack, pUseHairline );
1315 break;
1317 case MetaActionType::POINT:
1319 MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
1320 ImplActionBounds( aBound,
1321 tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1322 aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1323 aClipStack, pUseHairline );
1325 break;
1327 case MetaActionType::LINE:
1329 MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
1330 Point aP1( pAct->GetStartPoint() ), aP2( pAct->GetEndPoint() );
1331 tools::Rectangle aRect( aP1, aP2 );
1332 aRect.Justify();
1334 if(pUseHairline)
1336 const LineInfo& rLineInfo = pAct->GetLineInfo();
1338 if(0 != rLineInfo.GetWidth())
1339 pUseHairline = nullptr;
1342 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1344 break;
1346 case MetaActionType::RECT:
1348 MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
1349 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1351 break;
1353 case MetaActionType::ROUNDRECT:
1355 MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
1356 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1358 break;
1360 case MetaActionType::ELLIPSE:
1362 MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
1363 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1365 break;
1367 case MetaActionType::ARC:
1369 MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
1370 // FIXME: this is imprecise
1371 // e.g. for small arcs the whole rectangle is WAY too large
1372 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1374 break;
1376 case MetaActionType::PIE:
1378 MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
1379 // FIXME: this is imprecise
1380 // e.g. for small arcs the whole rectangle is WAY too large
1381 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1383 break;
1385 case MetaActionType::CHORD:
1387 MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
1388 // FIXME: this is imprecise
1389 // e.g. for small arcs the whole rectangle is WAY too large
1390 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1392 break;
1394 case MetaActionType::POLYLINE:
1396 MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
1397 tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1399 if(pUseHairline)
1401 const LineInfo& rLineInfo = pAct->GetLineInfo();
1403 if(0 != rLineInfo.GetWidth())
1404 pUseHairline = nullptr;
1407 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1409 break;
1411 case MetaActionType::POLYGON:
1413 MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
1414 tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1415 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1417 break;
1419 case MetaActionType::POLYPOLYGON:
1421 MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
1422 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1423 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1425 break;
1427 case MetaActionType::TEXT:
1429 MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
1430 tools::Rectangle aRect;
1431 // hdu said base = index
1432 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen() );
1433 Point aPt( pAct->GetPoint() );
1434 aRect.Move( aPt.X(), aPt.Y() );
1435 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1437 break;
1439 case MetaActionType::TEXTARRAY:
1441 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
1442 tools::Rectangle aRect;
1443 // hdu said base = index
1444 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1445 0, pAct->GetDXArray() );
1446 Point aPt( pAct->GetPoint() );
1447 aRect.Move( aPt.X(), aPt.Y() );
1448 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1450 break;
1452 case MetaActionType::STRETCHTEXT:
1454 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
1455 tools::Rectangle aRect;
1456 // hdu said base = index
1457 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1458 pAct->GetWidth() );
1459 Point aPt( pAct->GetPoint() );
1460 aRect.Move( aPt.X(), aPt.Y() );
1461 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1463 break;
1465 case MetaActionType::TEXTLINE:
1467 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
1468 // measure a test string to get ascend and descent right
1469 static const sal_Unicode pStr[] = { 0xc4, 0x67, 0 };
1470 OUString aStr( pStr );
1472 tools::Rectangle aRect;
1473 aMapVDev->GetTextBoundRect( aRect, aStr, 0, 0, aStr.getLength() );
1474 Point aPt( pAct->GetStartPoint() );
1475 aRect.Move( aPt.X(), aPt.Y() );
1476 aRect.SetRight( aRect.Left() + pAct->GetWidth() );
1477 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1479 break;
1481 case MetaActionType::BMPSCALE:
1483 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1484 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1485 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1487 break;
1489 case MetaActionType::BMPSCALEPART:
1491 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1492 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1493 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1495 break;
1497 case MetaActionType::BMPEXSCALE:
1499 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1500 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1501 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1503 break;
1505 case MetaActionType::BMPEXSCALEPART:
1507 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1508 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1509 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1511 break;
1513 case MetaActionType::GRADIENT:
1515 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1516 tools::Rectangle aRect( pAct->GetRect() );
1517 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1519 break;
1521 case MetaActionType::GRADIENTEX:
1523 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1524 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1525 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1527 break;
1529 case MetaActionType::COMMENT:
1531 // nothing to do
1533 break;
1535 case MetaActionType::HATCH:
1537 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
1538 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1539 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1541 break;
1543 case MetaActionType::Transparent:
1545 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1546 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1547 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1549 break;
1551 case MetaActionType::FLOATTRANSPARENT:
1553 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1554 // MetaFloatTransparentAction is defined limiting its content Metafile
1555 // to its geometry definition(Point, Size), so use these directly
1556 const tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1557 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1559 break;
1561 case MetaActionType::EPS:
1563 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
1564 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1565 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1567 break;
1569 case MetaActionType::CLIPREGION:
1571 MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1572 if( pAct->IsClipping() )
1573 aClipStack.back() = OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() );
1574 else
1575 aClipStack.back() = tools::Rectangle();
1577 break;
1579 case MetaActionType::ISECTRECTCLIPREGION:
1581 MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1582 tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1583 if( aClipStack.back().IsEmpty() )
1584 aClipStack.back() = aRect;
1585 else
1586 aClipStack.back().Intersection( aRect );
1588 break;
1590 case MetaActionType::ISECTREGIONCLIPREGION:
1592 MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1593 tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1594 if( aClipStack.back().IsEmpty() )
1595 aClipStack.back() = aRect;
1596 else
1597 aClipStack.back().Intersection( aRect );
1599 break;
1601 case MetaActionType::BMP:
1603 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
1604 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1605 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1607 break;
1609 case MetaActionType::BMPEX:
1611 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
1612 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmapEx().GetSizePixel() ) );
1613 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1615 break;
1617 case MetaActionType::MASK:
1619 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
1620 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1621 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1623 break;
1625 case MetaActionType::MASKSCALE:
1627 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1628 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1629 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1631 break;
1633 case MetaActionType::MASKSCALEPART:
1635 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1636 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1637 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1639 break;
1641 case MetaActionType::WALLPAPER:
1643 MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
1644 tools::Rectangle aRect( pAct->GetRect() );
1645 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1647 break;
1649 case MetaActionType::TEXTRECT:
1651 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pAction);
1652 tools::Rectangle aRect( pAct->GetRect() );
1653 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1655 break;
1657 case MetaActionType::MOVECLIPREGION:
1659 MetaMoveClipRegionAction* pAct = static_cast<MetaMoveClipRegionAction*>(pAction);
1660 if( ! aClipStack.back().IsEmpty() )
1662 Size aDelta( pAct->GetHorzMove(), pAct->GetVertMove() );
1663 aDelta = OutputDevice::LogicToLogic( aDelta, aMapVDev->GetMapMode(), GetPrefMapMode() );
1664 aClipStack.back().Move( aDelta.Width(), aDelta.Width() );
1667 break;
1669 default:
1671 pAction->Execute( aMapVDev.get() );
1673 if( nActionType == MetaActionType::PUSH )
1675 MetaPushAction* pAct = static_cast<MetaPushAction*>(pAction);
1676 aPushFlagStack.push_back( pAct->GetFlags() );
1677 if( aPushFlagStack.back() & PushFlags::CLIPREGION )
1679 tools::Rectangle aRect( aClipStack.back() );
1680 aClipStack.push_back( aRect );
1683 else if( nActionType == MetaActionType::POP )
1685 // sanity check
1686 if( ! aPushFlagStack.empty() )
1688 if( aPushFlagStack.back() & PushFlags::CLIPREGION )
1690 if( aClipStack.size() > 1 )
1691 aClipStack.pop_back();
1693 aPushFlagStack.pop_back();
1697 break;
1700 return aBound;
1703 Color GDIMetaFile::ImplColAdjustFnc( const Color& rColor, const void* pColParam )
1705 return Color( rColor.GetTransparency(),
1706 static_cast<const ImplColAdjustParam*>(pColParam)->pMapR[ rColor.GetRed() ],
1707 static_cast<const ImplColAdjustParam*>(pColParam)->pMapG[ rColor.GetGreen() ],
1708 static_cast<const ImplColAdjustParam*>(pColParam)->pMapB[ rColor.GetBlue() ] );
1712 BitmapEx GDIMetaFile::ImplBmpAdjustFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1714 const ImplBmpAdjustParam* p = static_cast<const ImplBmpAdjustParam*>(pBmpParam);
1715 BitmapEx aRet( rBmpEx );
1717 aRet.Adjust( p->nLuminancePercent, p->nContrastPercent,
1718 p->nChannelRPercent, p->nChannelGPercent, p->nChannelBPercent,
1719 p->fGamma, p->bInvert );
1721 return aRet;
1724 Color GDIMetaFile::ImplColConvertFnc( const Color& rColor, const void* pColParam )
1726 sal_uInt8 cLum = rColor.GetLuminance();
1728 if( MtfConversion::N1BitThreshold == static_cast<const ImplColConvertParam*>(pColParam)->eConversion )
1729 cLum = ( cLum < 128 ) ? 0 : 255;
1731 return Color( rColor.GetTransparency(), cLum, cLum, cLum );
1734 BitmapEx GDIMetaFile::ImplBmpConvertFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1736 BitmapEx aRet( rBmpEx );
1738 aRet.Convert( static_cast<const ImplBmpConvertParam*>(pBmpParam)->eConversion );
1740 return aRet;
1743 Color GDIMetaFile::ImplColMonoFnc( const Color&, const void* pColParam )
1745 return static_cast<const ImplColMonoParam*>(pColParam)->aColor;
1748 BitmapEx GDIMetaFile::ImplBmpMonoFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1750 BitmapPalette aPal( 3 );
1752 aPal[ 0 ] = COL_BLACK;
1753 aPal[ 1 ] = COL_WHITE;
1754 aPal[ 2 ] = static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor;
1756 Bitmap aBmp( rBmpEx.GetSizePixel(), 4, &aPal );
1757 aBmp.Erase( static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor );
1759 if( rBmpEx.IsAlpha() )
1760 return BitmapEx( aBmp, rBmpEx.GetAlpha() );
1761 else if( rBmpEx.IsTransparent() )
1762 return BitmapEx( aBmp, rBmpEx.GetMask() );
1763 else
1764 return BitmapEx( aBmp );
1767 Color GDIMetaFile::ImplColReplaceFnc( const Color& rColor, const void* pColParam )
1769 const sal_uLong nR = rColor.GetRed(), nG = rColor.GetGreen(), nB = rColor.GetBlue();
1771 for( sal_uLong i = 0; i < static_cast<const ImplColReplaceParam*>(pColParam)->nCount; i++ )
1773 if( ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinR[ i ] <= nR ) &&
1774 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxR[ i ] >= nR ) &&
1775 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinG[ i ] <= nG ) &&
1776 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxG[ i ] >= nG ) &&
1777 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinB[ i ] <= nB ) &&
1778 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxB[ i ] >= nB ) )
1780 return static_cast<const ImplColReplaceParam*>(pColParam)->pDstCols[ i ];
1784 return rColor;
1787 BitmapEx GDIMetaFile::ImplBmpReplaceFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1789 const ImplBmpReplaceParam* p = static_cast<const ImplBmpReplaceParam*>(pBmpParam);
1790 BitmapEx aRet( rBmpEx );
1792 aRet.Replace( p->pSrcCols, p->pDstCols, p->nCount );
1794 return aRet;
1797 void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pColParam,
1798 BmpExchangeFnc pFncBmp, const void* pBmpParam )
1800 GDIMetaFile aMtf;
1802 aMtf.m_aPrefSize = m_aPrefSize;
1803 aMtf.m_aPrefMapMode = m_aPrefMapMode;
1804 aMtf.m_bUseCanvas = m_bUseCanvas;
1806 for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
1808 const MetaActionType nType = pAction->GetType();
1810 switch( nType )
1812 case MetaActionType::PIXEL:
1814 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1815 aMtf.push_back( new MetaPixelAction( pAct->GetPoint(), pFncCol( pAct->GetColor(), pColParam ) ) );
1817 break;
1819 case MetaActionType::LINECOLOR:
1821 MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction);
1823 if( pAct->IsSetting() )
1824 pAct = new MetaLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1826 aMtf.push_back( pAct );
1828 break;
1830 case MetaActionType::FILLCOLOR:
1832 MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction);
1834 if( pAct->IsSetting() )
1835 pAct = new MetaFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1837 aMtf.push_back( pAct );
1839 break;
1841 case MetaActionType::TEXTCOLOR:
1843 MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction);
1844 aMtf.push_back( new MetaTextColorAction( pFncCol( pAct->GetColor(), pColParam ) ) );
1846 break;
1848 case MetaActionType::TEXTFILLCOLOR:
1850 MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction);
1852 if( pAct->IsSetting() )
1853 pAct = new MetaTextFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1855 aMtf.push_back( pAct );
1857 break;
1859 case MetaActionType::TEXTLINECOLOR:
1861 MetaTextLineColorAction* pAct = static_cast<MetaTextLineColorAction*>(pAction);
1863 if( pAct->IsSetting() )
1864 pAct = new MetaTextLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1866 aMtf.push_back( pAct );
1868 break;
1870 case MetaActionType::OVERLINECOLOR:
1872 MetaOverlineColorAction* pAct = static_cast<MetaOverlineColorAction*>(pAction);
1874 if( pAct->IsSetting() )
1875 pAct = new MetaOverlineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1877 aMtf.push_back( pAct );
1879 break;
1881 case MetaActionType::FONT:
1883 MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1884 vcl::Font aFont( pAct->GetFont() );
1886 aFont.SetColor( pFncCol( aFont.GetColor(), pColParam ) );
1887 aFont.SetFillColor( pFncCol( aFont.GetFillColor(), pColParam ) );
1888 aMtf.push_back( new MetaFontAction( aFont ) );
1890 break;
1892 case MetaActionType::WALLPAPER:
1894 MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
1895 Wallpaper aWall( pAct->GetWallpaper() );
1896 const tools::Rectangle& rRect = pAct->GetRect();
1898 aWall.SetColor( pFncCol( aWall.GetColor(), pColParam ) );
1900 if( aWall.IsBitmap() )
1901 aWall.SetBitmap( pFncBmp( aWall.GetBitmap(), pBmpParam ) );
1903 if( aWall.IsGradient() )
1905 Gradient aGradient( aWall.GetGradient() );
1907 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
1908 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
1909 aWall.SetGradient( aGradient );
1912 aMtf.push_back( new MetaWallpaperAction( rRect, aWall ) );
1914 break;
1916 case MetaActionType::BMP:
1917 case MetaActionType::BMPEX:
1918 case MetaActionType::MASK:
1920 OSL_FAIL( "Don't use bitmap actions of this type in metafiles!" );
1922 break;
1924 case MetaActionType::BMPSCALE:
1926 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1927 aMtf.push_back( new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(),
1928 pFncBmp( BitmapEx(pAct->GetBitmap()), pBmpParam ).GetBitmap() ) );
1930 break;
1932 case MetaActionType::BMPSCALEPART:
1934 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1935 aMtf.push_back( new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1936 pAct->GetSrcPoint(), pAct->GetSrcSize(),
1937 pFncBmp( BitmapEx(pAct->GetBitmap()), pBmpParam ).GetBitmap() )
1940 break;
1942 case MetaActionType::BMPEXSCALE:
1944 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1945 aMtf.push_back( new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(),
1946 pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
1949 break;
1951 case MetaActionType::BMPEXSCALEPART:
1953 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1954 aMtf.push_back( new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1955 pAct->GetSrcPoint(), pAct->GetSrcSize(),
1956 pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
1959 break;
1961 case MetaActionType::MASKSCALE:
1963 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
1964 aMtf.push_back( new MetaMaskScaleAction( pAct->GetPoint(), pAct->GetSize(),
1965 pAct->GetBitmap(),
1966 pFncCol( pAct->GetColor(), pColParam ) )
1969 break;
1971 case MetaActionType::MASKSCALEPART:
1973 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1974 aMtf.push_back( new MetaMaskScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1975 pAct->GetSrcPoint(), pAct->GetSrcSize(),
1976 pAct->GetBitmap(),
1977 pFncCol( pAct->GetColor(), pColParam ) )
1980 break;
1982 case MetaActionType::GRADIENT:
1984 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1985 Gradient aGradient( pAct->GetGradient() );
1987 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
1988 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
1989 aMtf.push_back( new MetaGradientAction( pAct->GetRect(), aGradient ) );
1991 break;
1993 case MetaActionType::GRADIENTEX:
1995 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1996 Gradient aGradient( pAct->GetGradient() );
1998 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
1999 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
2000 aMtf.push_back( new MetaGradientExAction( pAct->GetPolyPolygon(), aGradient ) );
2002 break;
2004 case MetaActionType::HATCH:
2006 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
2007 Hatch aHatch( pAct->GetHatch() );
2009 aHatch.SetColor( pFncCol( aHatch.GetColor(), pColParam ) );
2010 aMtf.push_back( new MetaHatchAction( pAct->GetPolyPolygon(), aHatch ) );
2012 break;
2014 case MetaActionType::FLOATTRANSPARENT:
2016 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
2017 GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
2019 aTransMtf.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2020 aMtf.push_back( new MetaFloatTransparentAction( aTransMtf,
2021 pAct->GetPoint(), pAct->GetSize(),
2022 pAct->GetGradient() )
2025 break;
2027 case MetaActionType::EPS:
2029 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
2030 GDIMetaFile aSubst( pAct->GetSubstitute() );
2032 aSubst.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2033 aMtf.push_back( new MetaEPSAction( pAct->GetPoint(), pAct->GetSize(),
2034 pAct->GetLink(), aSubst )
2037 break;
2039 default:
2041 aMtf.push_back( pAction );
2043 break;
2047 *this = aMtf;
2050 void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
2051 short nChannelRPercent, short nChannelGPercent,
2052 short nChannelBPercent, double fGamma, bool bInvert, bool msoBrightness )
2054 // nothing to do? => return quickly
2055 if( !(nLuminancePercent || nContrastPercent ||
2056 nChannelRPercent || nChannelGPercent || nChannelBPercent ||
2057 ( fGamma != 1.0 ) || bInvert) )
2058 return;
2060 double fM, fROff, fGOff, fBOff, fOff;
2061 ImplColAdjustParam aColParam;
2062 ImplBmpAdjustParam aBmpParam;
2064 aColParam.pMapR.reset(new sal_uInt8[ 256 ]);
2065 aColParam.pMapG.reset(new sal_uInt8[ 256 ]);
2066 aColParam.pMapB.reset(new sal_uInt8[ 256 ]);
2068 // calculate slope
2069 if( nContrastPercent >= 0 )
2070 fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0, 100 ) );
2071 else
2072 fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100, 0 ) ) / 128.0;
2074 if(!msoBrightness)
2075 // total offset = luminance offset + contrast offset
2076 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55 + 128.0 - fM * 128.0;
2077 else
2078 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55;
2080 // channel offset = channel offset + total offset
2081 fROff = nChannelRPercent * 2.55 + fOff;
2082 fGOff = nChannelGPercent * 2.55 + fOff;
2083 fBOff = nChannelBPercent * 2.55 + fOff;
2085 // calculate gamma value
2086 fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
2087 const bool bGamma = ( fGamma != 1.0 );
2089 // create mapping table
2090 for( long nX = 0; nX < 256; nX++ )
2092 if(!msoBrightness)
2094 aColParam.pMapR[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fROff ), 0, 255 ));
2095 aColParam.pMapG[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fGOff ), 0, 255 ));
2096 aColParam.pMapB[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fBOff ), 0, 255 ));
2098 else
2100 aColParam.pMapR[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fROff/2-128) * fM + 128 + fROff/2 ), 0, 255 ));
2101 aColParam.pMapG[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fGOff/2-128) * fM + 128 + fGOff/2 ), 0, 255 ));
2102 aColParam.pMapB[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fBOff/2-128) * fM + 128 + fBOff/2 ), 0, 255 ));
2104 if( bGamma )
2106 aColParam.pMapR[ nX ] = GAMMA( aColParam.pMapR[ nX ], fGamma );
2107 aColParam.pMapG[ nX ] = GAMMA( aColParam.pMapG[ nX ], fGamma );
2108 aColParam.pMapB[ nX ] = GAMMA( aColParam.pMapB[ nX ], fGamma );
2111 if( bInvert )
2113 aColParam.pMapR[ nX ] = ~aColParam.pMapR[ nX ];
2114 aColParam.pMapG[ nX ] = ~aColParam.pMapG[ nX ];
2115 aColParam.pMapB[ nX ] = ~aColParam.pMapB[ nX ];
2119 aBmpParam.nLuminancePercent = nLuminancePercent;
2120 aBmpParam.nContrastPercent = nContrastPercent;
2121 aBmpParam.nChannelRPercent = nChannelRPercent;
2122 aBmpParam.nChannelGPercent = nChannelGPercent;
2123 aBmpParam.nChannelBPercent = nChannelBPercent;
2124 aBmpParam.fGamma = fGamma;
2125 aBmpParam.bInvert = bInvert;
2127 // do color adjustment
2128 ImplExchangeColors( ImplColAdjustFnc, &aColParam, ImplBmpAdjustFnc, &aBmpParam );
2131 void GDIMetaFile::Convert( MtfConversion eConversion )
2133 ImplColConvertParam aColParam;
2134 ImplBmpConvertParam aBmpParam;
2136 aColParam.eConversion = eConversion;
2137 aBmpParam.eConversion = ( MtfConversion::N1BitThreshold == eConversion ) ? BmpConversion::N1BitThreshold : BmpConversion::N8BitGreys;
2139 ImplExchangeColors( ImplColConvertFnc, &aColParam, ImplBmpConvertFnc, &aBmpParam );
2142 void GDIMetaFile::ReplaceColors( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount )
2144 ImplColReplaceParam aColParam;
2145 ImplBmpReplaceParam aBmpParam;
2147 aColParam.pMinR.reset(new sal_uLong[ nColorCount ]);
2148 aColParam.pMaxR.reset(new sal_uLong[ nColorCount ]);
2149 aColParam.pMinG.reset(new sal_uLong[ nColorCount ]);
2150 aColParam.pMaxG.reset(new sal_uLong[ nColorCount ]);
2151 aColParam.pMinB.reset(new sal_uLong[ nColorCount ]);
2152 aColParam.pMaxB.reset(new sal_uLong[ nColorCount ]);
2154 for( sal_uLong i = 0; i < nColorCount; i++ )
2156 long nVal;
2158 nVal = pSearchColors[ i ].GetRed();
2159 aColParam.pMinR[ i ] = static_cast<sal_uLong>(std::max( nVal, 0L ));
2160 aColParam.pMaxR[ i ] = static_cast<sal_uLong>(std::min( nVal, 255L ));
2162 nVal = pSearchColors[ i ].GetGreen();
2163 aColParam.pMinG[ i ] = static_cast<sal_uLong>(std::max( nVal, 0L ));
2164 aColParam.pMaxG[ i ] = static_cast<sal_uLong>(std::min( nVal, 255L ));
2166 nVal = pSearchColors[ i ].GetBlue();
2167 aColParam.pMinB[ i ] = static_cast<sal_uLong>(std::max( nVal, 0L ));
2168 aColParam.pMaxB[ i ] = static_cast<sal_uLong>(std::min( nVal, 255L ));
2171 aColParam.pDstCols = pReplaceColors;
2172 aColParam.nCount = nColorCount;
2174 aBmpParam.pSrcCols = pSearchColors;
2175 aBmpParam.pDstCols = pReplaceColors;
2176 aBmpParam.nCount = nColorCount;
2178 ImplExchangeColors( ImplColReplaceFnc, &aColParam, ImplBmpReplaceFnc, &aBmpParam );
2181 GDIMetaFile GDIMetaFile::GetMonochromeMtf( const Color& rColor ) const
2183 GDIMetaFile aRet( *this );
2185 ImplColMonoParam aColParam;
2186 ImplBmpMonoParam aBmpParam;
2188 aColParam.aColor = rColor;
2189 aBmpParam.aColor = rColor;
2191 aRet.ImplExchangeColors( ImplColMonoFnc, &aColParam, ImplBmpMonoFnc, &aBmpParam );
2193 return aRet;
2196 BitmapChecksum GDIMetaFile::GetChecksum() const
2198 GDIMetaFile aMtf;
2199 SvMemoryStream aMemStm( 65535, 65535 );
2200 ImplMetaWriteData aWriteData;
2201 SVBT16 aBT16;
2202 SVBT32 aBT32;
2203 BitmapChecksumOctetArray aBCOA;
2204 BitmapChecksum nCrc = 0;
2206 aWriteData.meActualCharSet = aMemStm.GetStreamCharSet();
2207 for( size_t i = 0, nObjCount = GetActionSize(); i < nObjCount; i++ )
2209 MetaAction* pAction = GetAction( i );
2211 switch( pAction->GetType() )
2213 case MetaActionType::BMP:
2215 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
2217 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2218 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2220 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2221 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2223 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2224 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2226 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2227 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2229 break;
2231 case MetaActionType::BMPSCALE:
2233 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
2235 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2236 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2238 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2239 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2241 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2242 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2244 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2245 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2247 Int32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2248 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2250 Int32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2251 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2253 break;
2255 case MetaActionType::BMPSCALEPART:
2257 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
2259 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2260 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2262 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2263 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2265 Int32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2266 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2268 Int32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2269 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2271 Int32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2272 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2274 Int32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2275 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2277 Int32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2278 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2280 Int32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2281 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2283 Int32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2284 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2286 Int32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2287 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2289 break;
2291 case MetaActionType::BMPEX:
2293 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
2295 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2296 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2298 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2299 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2301 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2302 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2304 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2305 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2307 break;
2309 case MetaActionType::BMPEXSCALE:
2311 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
2313 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2314 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2316 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2317 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2319 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2320 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2322 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2323 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2325 Int32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2326 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2328 Int32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2329 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2331 break;
2333 case MetaActionType::BMPEXSCALEPART:
2335 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
2337 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2338 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2340 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2341 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2343 Int32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2344 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2346 Int32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2347 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2349 Int32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2350 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2352 Int32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2353 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2355 Int32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2356 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2358 Int32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2359 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2361 Int32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2362 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2364 Int32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2365 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2367 break;
2369 case MetaActionType::MASK:
2371 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
2373 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2374 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2376 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2377 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2379 UInt32ToSVBT32( sal_uInt32(pAct->GetColor()), aBT32 );
2380 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2382 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2383 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2385 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2386 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2388 break;
2390 case MetaActionType::MASKSCALE:
2392 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
2394 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2395 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2397 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2398 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2400 UInt32ToSVBT32( sal_uInt32(pAct->GetColor()), aBT32 );
2401 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2403 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2404 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2406 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2407 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2409 Int32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2410 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2412 Int32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2413 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2415 break;
2417 case MetaActionType::MASKSCALEPART:
2419 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
2421 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2422 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2424 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2425 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2427 UInt32ToSVBT32( sal_uInt32(pAct->GetColor()), aBT32 );
2428 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2430 Int32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2431 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2433 Int32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2434 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2436 Int32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2437 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2439 Int32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2440 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2442 Int32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2443 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2445 Int32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2446 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2448 Int32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2449 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2451 Int32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2452 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2454 break;
2456 case MetaActionType::EPS :
2458 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
2459 nCrc = vcl_get_checksum( nCrc, pAct->GetLink().GetData(), pAct->GetLink().GetDataSize() );
2461 break;
2463 case MetaActionType::CLIPREGION :
2465 MetaClipRegionAction& rAct = static_cast<MetaClipRegionAction&>(*pAction);
2466 const vcl::Region& rRegion = rAct.GetRegion();
2468 if(rRegion.HasPolyPolygonOrB2DPolyPolygon())
2470 // It has shown that this is a possible bottleneck for checksum calculation.
2471 // In worst case a very expensive RegionHandle representation gets created.
2472 // In this case it's cheaper to use the PolyPolygon
2473 const basegfx::B2DPolyPolygon aPolyPolygon(rRegion.GetAsB2DPolyPolygon());
2474 SVBT64 aSVBT64;
2476 for(auto const& rPolygon : aPolyPolygon)
2478 const sal_uInt32 nPointCount(rPolygon.count());
2479 const bool bControl(rPolygon.areControlPointsUsed());
2481 for(sal_uInt32 b(0); b < nPointCount; b++)
2483 const basegfx::B2DPoint aPoint(rPolygon.getB2DPoint(b));
2485 DoubleToSVBT64(aPoint.getX(), aSVBT64);
2486 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2487 DoubleToSVBT64(aPoint.getY(), aSVBT64);
2488 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2490 if(bControl)
2492 if(rPolygon.isPrevControlPointUsed(b))
2494 const basegfx::B2DPoint aCtrl(rPolygon.getPrevControlPoint(b));
2496 DoubleToSVBT64(aCtrl.getX(), aSVBT64);
2497 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2498 DoubleToSVBT64(aCtrl.getY(), aSVBT64);
2499 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2502 if(rPolygon.isNextControlPointUsed(b))
2504 const basegfx::B2DPoint aCtrl(rPolygon.getNextControlPoint(b));
2506 DoubleToSVBT64(aCtrl.getX(), aSVBT64);
2507 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2508 DoubleToSVBT64(aCtrl.getY(), aSVBT64);
2509 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2515 sal_uInt8 tmp = static_cast<sal_uInt8>(rAct.IsClipping());
2516 nCrc = vcl_get_checksum(nCrc, &tmp, 1);
2518 else
2520 pAction->Write( aMemStm, &aWriteData );
2521 nCrc = vcl_get_checksum( nCrc, aMemStm.GetData(), aMemStm.Tell() );
2522 aMemStm.Seek( 0 );
2525 break;
2527 default:
2529 pAction->Write( aMemStm, &aWriteData );
2530 nCrc = vcl_get_checksum( nCrc, aMemStm.GetData(), aMemStm.Tell() );
2531 aMemStm.Seek( 0 );
2533 break;
2537 return nCrc;
2540 sal_uLong GDIMetaFile::GetSizeBytes() const
2542 sal_uLong nSizeBytes = 0;
2544 for( size_t i = 0, nObjCount = GetActionSize(); i < nObjCount; ++i )
2546 MetaAction* pAction = GetAction( i );
2548 // default action size is set to 32 (=> not the exact value)
2549 nSizeBytes += 32;
2551 // add sizes for large action content
2552 switch( pAction->GetType() )
2554 case MetaActionType::BMP: nSizeBytes += static_cast<MetaBmpAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2555 case MetaActionType::BMPSCALE: nSizeBytes += static_cast<MetaBmpScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2556 case MetaActionType::BMPSCALEPART: nSizeBytes += static_cast<MetaBmpScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2558 case MetaActionType::BMPEX: nSizeBytes += static_cast<MetaBmpExAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2559 case MetaActionType::BMPEXSCALE: nSizeBytes += static_cast<MetaBmpExScaleAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2560 case MetaActionType::BMPEXSCALEPART: nSizeBytes += static_cast<MetaBmpExScalePartAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2562 case MetaActionType::MASK: nSizeBytes += static_cast<MetaMaskAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2563 case MetaActionType::MASKSCALE: nSizeBytes += static_cast<MetaMaskScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2564 case MetaActionType::MASKSCALEPART: nSizeBytes += static_cast<MetaMaskScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2566 case MetaActionType::POLYLINE: nSizeBytes += static_cast<MetaPolyLineAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2567 case MetaActionType::POLYGON: nSizeBytes += static_cast<MetaPolygonAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2568 case MetaActionType::POLYPOLYGON:
2570 const tools::PolyPolygon& rPolyPoly = static_cast<MetaPolyPolygonAction*>( pAction )->GetPolyPolygon();
2572 for( sal_uInt16 n = 0; n < rPolyPoly.Count(); ++n )
2573 nSizeBytes += ( rPolyPoly[ n ].GetSize() * sizeof( Point ) );
2575 break;
2577 case MetaActionType::TEXT: nSizeBytes += static_cast<MetaTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2578 case MetaActionType::STRETCHTEXT: nSizeBytes += static_cast<MetaStretchTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2579 case MetaActionType::TEXTRECT: nSizeBytes += static_cast<MetaTextRectAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2580 case MetaActionType::TEXTARRAY:
2582 MetaTextArrayAction* pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
2584 nSizeBytes += ( pTextArrayAction->GetText().getLength() * sizeof( sal_Unicode ) );
2586 if( pTextArrayAction->GetDXArray() )
2587 nSizeBytes += ( pTextArrayAction->GetLen() << 2 );
2589 break;
2590 default: break;
2594 return nSizeBytes;
2597 namespace
2599 class DepthGuard
2601 private:
2602 ImplMetaReadData& m_rData;
2603 rtl_TextEncoding const m_eOrigCharSet;
2604 public:
2605 DepthGuard(ImplMetaReadData& rData, SvStream const & rIStm)
2606 : m_rData(rData)
2607 , m_eOrigCharSet(m_rData.meActualCharSet)
2609 ++m_rData.mnParseDepth;
2610 m_rData.meActualCharSet = rIStm.GetStreamCharSet();
2612 bool TooDeep() const { return m_rData.mnParseDepth > 1024; }
2613 ~DepthGuard()
2615 --m_rData.mnParseDepth;
2616 m_rData.meActualCharSet = m_eOrigCharSet;
2621 SvStream& ReadGDIMetaFile(SvStream& rIStm, GDIMetaFile& rGDIMetaFile, ImplMetaReadData* pData)
2623 if (rIStm.GetError())
2625 SAL_WARN("vcl.gdi", "Stream error: " << rIStm.GetError());
2626 return rIStm;
2629 sal_uLong nStmPos = rIStm.Tell();
2630 SvStreamEndian nOldFormat = rIStm.GetEndian();
2632 rIStm.SetEndian( SvStreamEndian::LITTLE );
2636 char aId[7];
2637 aId[0] = 0;
2638 aId[6] = 0;
2639 rIStm.ReadBytes( aId, 6 );
2641 if ( !strcmp( aId, "VCLMTF" ) )
2643 // new format
2644 sal_uInt32 nStmCompressMode = 0;
2645 sal_uInt32 nCount = 0;
2646 std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rIStm, StreamMode::READ ));
2648 rIStm.ReadUInt32( nStmCompressMode );
2649 ReadMapMode( rIStm, rGDIMetaFile.m_aPrefMapMode );
2650 TypeSerializer aSerializer(rIStm);
2651 aSerializer.readSize(rGDIMetaFile.m_aPrefSize);
2652 rIStm.ReadUInt32( nCount );
2654 pCompat.reset(); // destructor writes stuff into the header
2656 std::unique_ptr<ImplMetaReadData> xReadData;
2657 if (!pData)
2659 xReadData.reset(new ImplMetaReadData);
2660 pData = xReadData.get();
2662 DepthGuard aDepthGuard(*pData, rIStm);
2664 if (aDepthGuard.TooDeep())
2665 throw std::runtime_error("too much recursion");
2667 for( sal_uInt32 nAction = 0; ( nAction < nCount ) && !rIStm.eof(); nAction++ )
2669 MetaAction* pAction = MetaAction::ReadMetaAction(rIStm, pData);
2670 if( pAction )
2672 if (pAction->GetType() == MetaActionType::COMMENT)
2674 MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction);
2675 if ( pCommentAct->GetComment() == "EMF_PLUS" )
2676 rGDIMetaFile.UseCanvas( true );
2678 rGDIMetaFile.AddAction( pAction );
2682 else
2684 rIStm.Seek( nStmPos );
2685 SVMConverter( rIStm, rGDIMetaFile );
2688 catch (...)
2690 SAL_WARN("vcl", "GDIMetaFile exception during load");
2691 rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
2694 // check for errors
2695 if( rIStm.GetError() )
2697 rGDIMetaFile.Clear();
2698 rIStm.Seek( nStmPos );
2701 rIStm.SetEndian( nOldFormat );
2702 return rIStm;
2705 SvStream& WriteGDIMetaFile( SvStream& rOStm, const GDIMetaFile& rGDIMetaFile )
2707 if( !rOStm.GetError() )
2709 const_cast< GDIMetaFile& >( rGDIMetaFile ).Write( rOStm );
2711 return rOStm;
2714 SvStream& GDIMetaFile::Read( SvStream& rIStm )
2716 Clear();
2717 ReadGDIMetaFile( rIStm, *this );
2719 return rIStm;
2722 SvStream& GDIMetaFile::Write( SvStream& rOStm )
2724 VersionCompat* pCompat;
2725 const SvStreamCompressFlags nStmCompressMode = rOStm.GetCompressMode();
2726 SvStreamEndian nOldFormat = rOStm.GetEndian();
2728 rOStm.SetEndian( SvStreamEndian::LITTLE );
2729 rOStm.WriteBytes( "VCLMTF", 6 );
2731 pCompat = new VersionCompat( rOStm, StreamMode::WRITE, 1 );
2733 rOStm.WriteUInt32( static_cast<sal_uInt32>(nStmCompressMode) );
2734 WriteMapMode( rOStm, m_aPrefMapMode );
2735 TypeSerializer aSerializer(rOStm);
2736 aSerializer.writeSize(m_aPrefSize);
2737 rOStm.WriteUInt32( GetActionSize() );
2739 delete pCompat;
2741 ImplMetaWriteData aWriteData;
2743 aWriteData.meActualCharSet = rOStm.GetStreamCharSet();
2745 MetaAction* pAct = FirstAction();
2746 while ( pAct )
2748 pAct->Write( rOStm, &aWriteData );
2749 pAct = NextAction();
2752 rOStm.SetEndian( nOldFormat );
2754 return rOStm;
2757 bool GDIMetaFile::CreateThumbnail(BitmapEx& rBitmapEx, BmpConversion eColorConversion, BmpScaleFlag nScaleFlag) const
2759 // initialization seems to be complicated but is used to avoid rounding errors
2760 ScopedVclPtrInstance< VirtualDevice > aVDev;
2761 const Point aNullPt;
2762 const Point aTLPix( aVDev->LogicToPixel( aNullPt, GetPrefMapMode() ) );
2763 const Point aBRPix( aVDev->LogicToPixel( Point( GetPrefSize().Width() - 1, GetPrefSize().Height() - 1 ), GetPrefMapMode() ) );
2764 Size aDrawSize( aVDev->LogicToPixel( GetPrefSize(), GetPrefMapMode() ) );
2765 Size aSizePix( labs( aBRPix.X() - aTLPix.X() ) + 1, labs( aBRPix.Y() - aTLPix.Y() ) + 1 );
2766 sal_uInt32 nMaximumExtent = 256;
2768 if (!rBitmapEx.IsEmpty())
2769 rBitmapEx.SetEmpty();
2771 // determine size that has the same aspect ratio as image size and
2772 // fits into the rectangle determined by nMaximumExtent
2773 if ( aSizePix.Width() && aSizePix.Height()
2774 && ( sal::static_int_cast< unsigned long >(aSizePix.Width()) >
2775 nMaximumExtent ||
2776 sal::static_int_cast< unsigned long >(aSizePix.Height()) >
2777 nMaximumExtent ) )
2779 const Size aOldSizePix( aSizePix );
2780 double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height();
2782 if ( fWH <= 1.0 )
2784 aSizePix.setWidth( FRound( nMaximumExtent * fWH ) );
2785 aSizePix.setHeight( nMaximumExtent );
2787 else
2789 aSizePix.setWidth( nMaximumExtent );
2790 aSizePix.setHeight( FRound( nMaximumExtent / fWH ) );
2793 aDrawSize.setWidth( FRound( ( static_cast< double >( aDrawSize.Width() ) * aSizePix.Width() ) / aOldSizePix.Width() ) );
2794 aDrawSize.setHeight( FRound( ( static_cast< double >( aDrawSize.Height() ) * aSizePix.Height() ) / aOldSizePix.Height() ) );
2797 // draw image(s) into VDev and get resulting image
2798 // do it 4x larger to be able to scale it down & get beautiful antialias
2799 Size aAntialiasSize(aSizePix.Width() * 4, aSizePix.Height() * 4);
2800 if (aVDev->SetOutputSizePixel(aAntialiasSize))
2802 // antialias: provide 4x larger size, and then scale down the result
2803 Size aAntialias(aDrawSize.Width() * 4, aDrawSize.Height() * 4);
2805 // draw metafile into VDev
2806 const_cast<GDIMetaFile *>(this)->WindStart();
2807 const_cast<GDIMetaFile *>(this)->Play(aVDev.get(), Point(), aAntialias);
2809 // get paint bitmap
2810 BitmapEx aBitmap( aVDev->GetBitmapEx( aNullPt, aVDev->GetOutputSizePixel() ) );
2812 // scale down the image to the desired size - use the input scaler for the scaling operation
2813 aBitmap.Scale(aDrawSize, nScaleFlag);
2815 // convert to desired bitmap color format
2816 Size aSize(aBitmap.GetSizePixel());
2817 if (aSize.Width() && aSize.Height())
2818 aBitmap.Convert(eColorConversion);
2820 rBitmapEx = aBitmap;
2823 return !rBitmapEx.IsEmpty();
2826 void GDIMetaFile::UseCanvas( bool _bUseCanvas )
2828 m_bUseCanvas = _bUseCanvas;
2831 void GDIMetaFile::dumpAsXml(const char* pFileName) const
2833 SvFileStream aStream(pFileName ? OUString::fromUtf8(pFileName) : OUString("file:///tmp/metafile.xml"),
2834 StreamMode::STD_READWRITE | StreamMode::TRUNC);
2835 assert(aStream.good());
2836 MetafileXmlDump aDumper;
2837 aDumper.dump(*this, aStream);
2840 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */