bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / gdi / gdimtf.cxx
blobcceaef8b9afd934f70a824314d38ae194719aee4
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 <vcl/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 namespace {
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;
113 GDIMetaFile::GDIMetaFile() :
114 m_nCurrentActionElement( 0 ),
115 m_aPrefSize ( 1, 1 ),
116 m_pPrev ( nullptr ),
117 m_pNext ( nullptr ),
118 m_pOutDev ( nullptr ),
119 m_bPause ( false ),
120 m_bRecord ( false ),
121 m_bUseCanvas ( false )
125 GDIMetaFile::GDIMetaFile( const GDIMetaFile& rMtf ) :
126 m_nCurrentActionElement( rMtf.m_nCurrentActionElement ),
127 m_aPrefMapMode ( rMtf.m_aPrefMapMode ),
128 m_aPrefSize ( rMtf.m_aPrefSize ),
129 m_pPrev ( rMtf.m_pPrev ),
130 m_pNext ( rMtf.m_pNext ),
131 m_pOutDev ( nullptr ),
132 m_bPause ( false ),
133 m_bRecord ( false ),
134 m_bUseCanvas ( rMtf.m_bUseCanvas )
136 for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
138 m_aList.push_back( rMtf.GetAction( i ) );
141 if( rMtf.m_bRecord )
143 Record( rMtf.m_pOutDev );
145 if ( rMtf.m_bPause )
146 Pause( true );
150 GDIMetaFile::~GDIMetaFile()
152 Clear();
155 bool GDIMetaFile::HasTransparentActions() const
157 MetaAction* pCurrAct;
159 // watch for transparent drawing actions
160 for(pCurrAct = const_cast<GDIMetaFile*>(this)->FirstAction();
161 pCurrAct;
162 pCurrAct = const_cast<GDIMetaFile*>(this)->NextAction())
164 // #i10613# determine if the action is transparency capable
166 // #107169# Also examine metafiles with masked bitmaps in
167 // detail. Further down, this is optimized in such a way
168 // that there's no unnecessary painting of masked bitmaps
169 // (which are _always_ subdivided into rectangular regions
170 // of uniform opacity): if a masked bitmap is printed over
171 // empty background, we convert to a plain bitmap with
172 // white background.
173 if (pCurrAct->IsTransparent())
174 return true;
177 return false;
180 size_t GDIMetaFile::GetActionSize() const
182 return m_aList.size();
185 MetaAction* GDIMetaFile::GetAction( size_t nAction ) const
187 return (nAction < m_aList.size()) ? m_aList[ nAction ].get() : nullptr;
190 MetaAction* GDIMetaFile::FirstAction()
192 m_nCurrentActionElement = 0;
193 return m_aList.empty() ? nullptr : m_aList[ 0 ].get();
196 MetaAction* GDIMetaFile::NextAction()
198 return ( m_nCurrentActionElement + 1 < m_aList.size() ) ? m_aList[ ++m_nCurrentActionElement ].get() : nullptr;
201 void GDIMetaFile::ReplaceAction( rtl::Reference<MetaAction> pAction, size_t nAction )
203 if ( nAction >= m_aList.size() )
205 return;
207 //fdo#39995 This doesn't increment the incoming action ref-count nor does it
208 //decrement the outgoing action ref-count
209 std::swap(pAction, m_aList[nAction]);
212 GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf )
214 if( this != &rMtf )
216 Clear();
218 // Increment RefCount of MetaActions
219 for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
221 m_aList.push_back( rMtf.GetAction( i ) );
224 m_aPrefMapMode = rMtf.m_aPrefMapMode;
225 m_aPrefSize = rMtf.m_aPrefSize;
226 m_pPrev = rMtf.m_pPrev;
227 m_pNext = rMtf.m_pNext;
228 m_pOutDev = nullptr;
229 m_bPause = false;
230 m_bRecord = false;
231 m_bUseCanvas = rMtf.m_bUseCanvas;
233 if( rMtf.m_bRecord )
235 Record( rMtf.m_pOutDev );
237 if( rMtf.m_bPause )
238 Pause( true );
242 return *this;
245 bool GDIMetaFile::operator==( const GDIMetaFile& rMtf ) const
247 const size_t nObjCount = m_aList.size();
248 bool bRet = false;
250 if( this == &rMtf )
251 bRet = true;
252 else if( rMtf.GetActionSize() == nObjCount &&
253 rMtf.GetPrefSize() == m_aPrefSize &&
254 rMtf.GetPrefMapMode() == m_aPrefMapMode )
256 bRet = true;
258 for( size_t n = 0; n < nObjCount; n++ )
260 if( m_aList[ n ] != rMtf.GetAction( n ) )
262 bRet = false;
263 break;
268 return bRet;
271 void GDIMetaFile::Clear()
273 if( m_bRecord )
274 Stop();
276 m_aList.clear();
279 void GDIMetaFile::Linker( OutputDevice* pOut, bool bLink )
281 if( bLink )
283 m_pNext = nullptr;
284 m_pPrev = pOut->GetConnectMetaFile();
285 pOut->SetConnectMetaFile( this );
287 if( m_pPrev )
288 m_pPrev->m_pNext = this;
290 else
292 if( m_pNext )
294 m_pNext->m_pPrev = m_pPrev;
296 if( m_pPrev )
297 m_pPrev->m_pNext = m_pNext;
299 else
301 if( m_pPrev )
302 m_pPrev->m_pNext = nullptr;
304 pOut->SetConnectMetaFile( m_pPrev );
307 m_pPrev = nullptr;
308 m_pNext = nullptr;
312 void GDIMetaFile::Record( OutputDevice* pOut )
314 if( m_bRecord )
315 Stop();
317 m_nCurrentActionElement = m_aList.empty() ? 0 : (m_aList.size() - 1);
318 m_pOutDev = pOut;
319 m_bRecord = true;
320 Linker( pOut, true );
323 void GDIMetaFile::Play( GDIMetaFile& rMtf )
325 if (m_bRecord || rMtf.m_bRecord)
326 return;
328 MetaAction* pAction = GetCurAction();
329 const size_t nObjCount = m_aList.size();
331 rMtf.UseCanvas( rMtf.GetUseCanvas() || m_bUseCanvas );
333 for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nObjCount; nCurPos++ )
335 if( pAction )
337 rMtf.AddAction( pAction );
340 pAction = NextAction();
344 void GDIMetaFile::Play(OutputDevice& rOut, size_t nPos)
346 if( m_bRecord )
347 return;
349 MetaAction* pAction = GetCurAction();
350 const size_t nObjCount = m_aList.size();
351 size_t nSyncCount = rOut.GetSyncCount();
353 if( nPos > nObjCount )
354 nPos = nObjCount;
356 // #i23407# Set backwards-compatible text language and layout mode
357 // This is necessary, since old metafiles don't even know of these
358 // recent add-ons. Newer metafiles must of course explicitly set
359 // those states.
360 rOut.Push(PushFlags::TEXTLAYOUTMODE|PushFlags::TEXTLANGUAGE);
361 rOut.SetLayoutMode(ComplexTextLayoutFlags::Default);
362 rOut.SetDigitLanguage(LANGUAGE_SYSTEM);
364 SAL_INFO( "vcl.gdi", "GDIMetaFile::Play on device of size: " << rOut.GetOutputSizePixel().Width() << " " << rOut.GetOutputSizePixel().Height());
366 if (!ImplPlayWithRenderer(rOut, Point(0,0), rOut.GetOutputSize())) {
367 size_t i = 0;
368 for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nPos; nCurPos++ )
370 if( pAction )
372 pAction->Execute(&rOut);
374 // flush output from time to time
375 if( i++ > nSyncCount )
377 rOut.Flush();
378 i = 0;
382 pAction = NextAction();
385 rOut.Pop();
388 bool GDIMetaFile::ImplPlayWithRenderer(OutputDevice& rOut, const Point& rPos, Size rLogicDestSize)
390 if (!m_bUseCanvas)
391 return false;
393 Size rDestSize(rOut.LogicToPixel(rLogicDestSize));
395 const vcl::Window* win = rOut.GetOwnerWindow();
397 if (!win)
398 win = Application::GetActiveTopWindow();
399 if (!win)
400 win = Application::GetFirstTopLevelWindow();
402 if (!win)
403 return false;
407 uno::Reference<rendering::XCanvas> xCanvas = win->GetOutDev()->GetCanvas ();
409 if (!xCanvas.is())
410 return false;
412 Size aSize (rDestSize.Width () + 1, rDestSize.Height () + 1);
413 uno::Reference<rendering::XBitmap> xBitmap = xCanvas->getDevice ()->createCompatibleAlphaBitmap (vcl::unotools::integerSize2DFromSize( aSize));
414 if( xBitmap.is () )
416 uno::Reference< rendering::XBitmapCanvas > xBitmapCanvas( xBitmap, uno::UNO_QUERY );
417 if( xBitmapCanvas.is() )
419 uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
420 uno::Reference< rendering::XMtfRenderer > xMtfRenderer = rendering::MtfRenderer::createWithBitmapCanvas( xContext, xBitmapCanvas );
422 xBitmapCanvas->clear();
423 uno::Reference< beans::XFastPropertySet > xMtfFastPropertySet( xMtfRenderer, uno::UNO_QUERY );
424 if( xMtfFastPropertySet.is() )
425 // set this metafile to the renderer to
426 // speedup things (instead of copying data to
427 // sequence of bytes passed to renderer)
428 xMtfFastPropertySet->setFastPropertyValue( 0, uno::Any( reinterpret_cast<sal_Int64>( this ) ) );
430 xMtfRenderer->draw( rDestSize.Width(), rDestSize.Height() );
432 BitmapEx aBitmapEx;
433 if( aBitmapEx.Create( xBitmapCanvas, aSize ) )
435 if (rOut.GetMapMode().GetMapUnit() == MapUnit::MapPixel)
436 rOut.DrawBitmapEx( rPos, aBitmapEx );
437 else
438 rOut.DrawBitmapEx( rPos, rLogicDestSize, aBitmapEx );
439 return true;
444 catch (const uno::RuntimeException& )
446 throw; // runtime errors are fatal
448 catch (const uno::Exception&)
450 // ignore errors, no way of reporting them here
451 TOOLS_WARN_EXCEPTION("vcl.gdi", "GDIMetaFile::ImplPlayWithRenderer");
454 return false;
457 void GDIMetaFile::Play(OutputDevice& rOut, const Point& rPos,
458 const Size& rSize)
460 MapMode aDrawMap( GetPrefMapMode() );
461 Size aDestSize(rOut.LogicToPixel(rSize));
463 if( !aDestSize.Width() || !aDestSize.Height() )
464 return;
466 GDIMetaFile* pMtf = rOut.GetConnectMetaFile();
468 if (ImplPlayWithRenderer(rOut, rPos, rSize))
469 return;
471 Size aTmpPrefSize(rOut.LogicToPixel(GetPrefSize(), aDrawMap));
473 if( !aTmpPrefSize.Width() )
474 aTmpPrefSize.setWidth( aDestSize.Width() );
476 if( !aTmpPrefSize.Height() )
477 aTmpPrefSize.setHeight( aDestSize.Height() );
479 Fraction aScaleX( aDestSize.Width(), aTmpPrefSize.Width() );
480 Fraction aScaleY( aDestSize.Height(), aTmpPrefSize.Height() );
482 aScaleX *= aDrawMap.GetScaleX(); aDrawMap.SetScaleX( aScaleX );
483 aScaleY *= aDrawMap.GetScaleY(); aDrawMap.SetScaleY( aScaleY );
485 // #i47260# Convert logical output position to offset within
486 // the metafile's mapmode. Therefore, disable pixel offset on
487 // outdev, it's inverse mnOutOffLogicX/Y is calculated for a
488 // different mapmode (the one currently set on rOut, that is)
489 // - thus, aDrawMap's origin would generally be wrong. And
490 // even _if_ aDrawMap is similar to pOutDev's current mapmode,
491 // it's _still_ undesirable to have pixel offset unequal zero,
492 // because one would still get round-off errors (the
493 // round-trip error for LogicToPixel( PixelToLogic() ) was the
494 // reason for having pixel offset in the first place).
495 const Size& rOldOffset(rOut.GetPixelOffset());
496 const Size aEmptySize;
497 rOut.SetPixelOffset(aEmptySize);
498 aDrawMap.SetOrigin(rOut.PixelToLogic(rOut.LogicToPixel(rPos), aDrawMap));
499 rOut.SetPixelOffset(rOldOffset);
501 rOut.Push();
503 bool bIsRecord = (pMtf && pMtf->IsRecord());
504 rOut.SetMetafileMapMode(aDrawMap, bIsRecord);
506 // #i23407# Set backwards-compatible text language and layout mode
507 // This is necessary, since old metafiles don't even know of these
508 // recent add-ons. Newer metafiles must of course explicitly set
509 // those states.
510 rOut.SetLayoutMode(ComplexTextLayoutFlags::Default);
511 rOut.SetDigitLanguage(LANGUAGE_SYSTEM);
513 Play(rOut);
515 rOut.Pop();
518 void GDIMetaFile::Pause( bool _bPause )
520 if( !m_bRecord )
521 return;
523 if( _bPause )
525 if( !m_bPause )
526 Linker( m_pOutDev, false );
528 else
530 if( m_bPause )
531 Linker( m_pOutDev, true );
534 m_bPause = _bPause;
537 void GDIMetaFile::Stop()
539 if( m_bRecord )
541 m_bRecord = false;
543 if( !m_bPause )
544 Linker( m_pOutDev, false );
545 else
546 m_bPause = false;
550 void GDIMetaFile::WindStart()
552 if( !m_bRecord )
553 m_nCurrentActionElement = 0;
556 void GDIMetaFile::WindPrev()
558 if( !m_bRecord )
559 if ( m_nCurrentActionElement > 0 )
560 --m_nCurrentActionElement;
563 void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction)
565 m_aList.push_back( pAction );
567 if( m_pPrev )
569 m_pPrev->AddAction( pAction );
573 void GDIMetaFile::AddAction(const rtl::Reference<MetaAction>& pAction, size_t nPos)
575 if ( nPos < m_aList.size() )
577 m_aList.insert( m_aList.begin() + nPos, pAction );
579 else
581 m_aList.push_back( pAction );
584 if( m_pPrev )
586 m_pPrev->AddAction( pAction, nPos );
590 void GDIMetaFile::push_back(const rtl::Reference<MetaAction>& pAction)
592 m_aList.push_back( pAction );
595 void GDIMetaFile::Mirror( BmpMirrorFlags nMirrorFlags )
597 const Size aOldPrefSize( GetPrefSize() );
598 tools::Long nMoveX, nMoveY;
599 double fScaleX, fScaleY;
601 if( nMirrorFlags & BmpMirrorFlags::Horizontal )
603 nMoveX = std::abs( aOldPrefSize.Width() ) - 1;
604 fScaleX = -1.0;
606 else
608 nMoveX = 0;
609 fScaleX = 1.0;
612 if( nMirrorFlags & BmpMirrorFlags::Vertical )
614 nMoveY = std::abs( aOldPrefSize.Height() ) - 1;
615 fScaleY = -1.0;
617 else
619 nMoveY = 0;
620 fScaleY = 1.0;
623 if( ( fScaleX != 1.0 ) || ( fScaleY != 1.0 ) )
625 Scale( fScaleX, fScaleY );
626 Move( nMoveX, nMoveY );
627 SetPrefSize( aOldPrefSize );
631 void GDIMetaFile::Move( tools::Long nX, tools::Long nY )
633 const Size aBaseOffset( nX, nY );
634 Size aOffset( aBaseOffset );
635 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
637 aMapVDev->EnableOutput( false );
638 aMapVDev->SetMapMode( GetPrefMapMode() );
640 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
642 const MetaActionType nType = pAct->GetType();
643 MetaAction* pModAct;
645 if( pAct->GetRefCount() > 1 )
647 m_aList[ m_nCurrentActionElement ] = pAct->Clone();
648 pModAct = m_aList[ m_nCurrentActionElement ].get();
650 else
651 pModAct = pAct;
653 if( ( MetaActionType::MAPMODE == nType ) ||
654 ( MetaActionType::PUSH == nType ) ||
655 ( MetaActionType::POP == nType ) )
657 pModAct->Execute( aMapVDev.get() );
658 aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
661 pModAct->Move( aOffset.Width(), aOffset.Height() );
665 void GDIMetaFile::Move( tools::Long nX, tools::Long nY, tools::Long nDPIX, tools::Long nDPIY )
667 const Size aBaseOffset( nX, nY );
668 Size aOffset( aBaseOffset );
669 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
671 aMapVDev->EnableOutput( false );
672 aMapVDev->SetReferenceDevice( nDPIX, nDPIY );
673 aMapVDev->SetMapMode( GetPrefMapMode() );
675 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
677 const MetaActionType nType = pAct->GetType();
678 MetaAction* pModAct;
680 if( pAct->GetRefCount() > 1 )
682 m_aList[ m_nCurrentActionElement ] = pAct->Clone();
683 pModAct = m_aList[ m_nCurrentActionElement ].get();
685 else
686 pModAct = pAct;
688 if( ( MetaActionType::MAPMODE == nType ) ||
689 ( MetaActionType::PUSH == nType ) ||
690 ( MetaActionType::POP == nType ) )
692 pModAct->Execute( aMapVDev.get() );
693 if( aMapVDev->GetMapMode().GetMapUnit() == MapUnit::MapPixel )
695 aOffset = aMapVDev->LogicToPixel( aBaseOffset, GetPrefMapMode() );
696 MapMode aMap( aMapVDev->GetMapMode() );
697 aOffset.setWidth( static_cast<tools::Long>(aOffset.Width() * static_cast<double>(aMap.GetScaleX())) );
698 aOffset.setHeight( static_cast<tools::Long>(aOffset.Height() * static_cast<double>(aMap.GetScaleY())) );
700 else
701 aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
704 pModAct->Move( aOffset.Width(), aOffset.Height() );
708 void GDIMetaFile::Scale( double fScaleX, double fScaleY )
710 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
712 MetaAction* pModAct;
714 if( pAct->GetRefCount() > 1 )
716 m_aList[ m_nCurrentActionElement ] = pAct->Clone();
717 pModAct = m_aList[ m_nCurrentActionElement ].get();
719 else
720 pModAct = pAct;
722 pModAct->Scale( fScaleX, fScaleY );
725 m_aPrefSize.setWidth( FRound( m_aPrefSize.Width() * fScaleX ) );
726 m_aPrefSize.setHeight( FRound( m_aPrefSize.Height() * fScaleY ) );
729 void GDIMetaFile::Scale( const Fraction& rScaleX, const Fraction& rScaleY )
731 Scale( static_cast<double>(rScaleX), static_cast<double>(rScaleY) );
734 void GDIMetaFile::Clip( const tools::Rectangle& i_rClipRect )
736 tools::Rectangle aCurRect( i_rClipRect );
737 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
739 aMapVDev->EnableOutput( false );
740 aMapVDev->SetMapMode( GetPrefMapMode() );
742 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
744 const MetaActionType nType = pAct->GetType();
746 if( ( MetaActionType::MAPMODE == nType ) ||
747 ( MetaActionType::PUSH == nType ) ||
748 ( MetaActionType::POP == nType ) )
750 pAct->Execute( aMapVDev.get() );
751 aCurRect = OutputDevice::LogicToLogic( i_rClipRect, GetPrefMapMode(), aMapVDev->GetMapMode() );
753 else if( nType == MetaActionType::CLIPREGION )
755 MetaClipRegionAction* pOldAct = static_cast<MetaClipRegionAction*>(pAct);
756 vcl::Region aNewReg( aCurRect );
757 if( pOldAct->IsClipping() )
758 aNewReg.Intersect( pOldAct->GetRegion() );
759 MetaClipRegionAction* pNewAct = new MetaClipRegionAction( aNewReg, true );
760 m_aList[ m_nCurrentActionElement ] = pNewAct;
765 Point GDIMetaFile::ImplGetRotatedPoint( const Point& rPt, const Point& rRotatePt,
766 const Size& rOffset, double fSin, double fCos )
768 const tools::Long nX = rPt.X() - rRotatePt.X();
769 const tools::Long nY = rPt.Y() - rRotatePt.Y();
771 return Point( FRound( fCos * nX + fSin * nY ) + rRotatePt.X() + rOffset.Width(),
772 -FRound( fSin * nX - fCos * nY ) + rRotatePt.Y() + rOffset.Height() );
775 tools::Polygon GDIMetaFile::ImplGetRotatedPolygon( const tools::Polygon& rPoly, const Point& rRotatePt,
776 const Size& rOffset, double fSin, double fCos )
778 tools::Polygon aRet( rPoly );
780 aRet.Rotate( rRotatePt, fSin, fCos );
781 aRet.Move( rOffset.Width(), rOffset.Height() );
783 return aRet;
786 tools::PolyPolygon GDIMetaFile::ImplGetRotatedPolyPolygon( const tools::PolyPolygon& rPolyPoly, const Point& rRotatePt,
787 const Size& rOffset, double fSin, double fCos )
789 tools::PolyPolygon aRet( rPolyPoly );
791 aRet.Rotate( rRotatePt, fSin, fCos );
792 aRet.Move( rOffset.Width(), rOffset.Height() );
794 return aRet;
797 void GDIMetaFile::ImplAddGradientEx( GDIMetaFile& rMtf,
798 const OutputDevice& rMapDev,
799 const tools::PolyPolygon& rPolyPoly,
800 const Gradient& rGrad )
802 // Generate comment, GradientEx and Gradient actions (within DrawGradient)
803 ScopedVclPtrInstance< VirtualDevice > aVDev(rMapDev, DeviceFormat::DEFAULT);
804 aVDev->EnableOutput( false );
805 GDIMetaFile aGradMtf;
807 aGradMtf.Record( aVDev.get() );
808 aVDev->DrawGradient( rPolyPoly, rGrad );
809 aGradMtf.Stop();
811 size_t i, nAct( aGradMtf.GetActionSize() );
812 for( i=0; i < nAct; ++i )
814 MetaAction* pMetaAct = aGradMtf.GetAction( i );
815 rMtf.AddAction( pMetaAct );
819 void GDIMetaFile::Rotate( Degree10 nAngle10 )
821 nAngle10 %= 3600_deg10;
822 nAngle10 = ( nAngle10 < 0_deg10 ) ? ( Degree10(3599) + nAngle10 ) : nAngle10;
824 if( !nAngle10 )
825 return;
827 GDIMetaFile aMtf;
828 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
829 const double fAngle = F_PI1800 * nAngle10.get();
830 const double fSin = sin( fAngle );
831 const double fCos = cos( fAngle );
832 tools::Rectangle aRect( Point(), GetPrefSize() );
833 tools::Polygon aPoly( aRect );
835 aPoly.Rotate( Point(), fSin, fCos );
837 aMapVDev->EnableOutput( false );
838 aMapVDev->SetMapMode( GetPrefMapMode() );
840 const tools::Rectangle aNewBound( aPoly.GetBoundRect() );
842 const Point aOrigin( GetPrefMapMode().GetOrigin().X(), GetPrefMapMode().GetOrigin().Y() );
843 const Size aOffset( -aNewBound.Left(), -aNewBound.Top() );
845 Point aRotAnchor( aOrigin );
846 Size aRotOffset( aOffset );
848 for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
850 const MetaActionType nActionType = pAction->GetType();
852 switch( nActionType )
854 case MetaActionType::PIXEL:
856 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
857 aMtf.AddAction( new MetaPixelAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
858 pAct->GetColor() ) );
860 break;
862 case MetaActionType::POINT:
864 MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
865 aMtf.AddAction( new MetaPointAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
867 break;
869 case MetaActionType::LINE:
871 MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
872 aMtf.AddAction( new MetaLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
873 ImplGetRotatedPoint( pAct->GetEndPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
874 pAct->GetLineInfo() ) );
876 break;
878 case MetaActionType::RECT:
880 MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
881 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
883 break;
885 case MetaActionType::ROUNDRECT:
887 MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
888 const tools::Polygon aRoundRectPoly( pAct->GetRect(), pAct->GetHorzRound(), pAct->GetVertRound() );
890 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aRoundRectPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
892 break;
894 case MetaActionType::ELLIPSE:
896 MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
897 const tools::Polygon aEllipsePoly( pAct->GetRect().Center(), pAct->GetRect().GetWidth() >> 1, pAct->GetRect().GetHeight() >> 1 );
899 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aEllipsePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
901 break;
903 case MetaActionType::ARC:
905 MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
906 const tools::Polygon aArcPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Arc );
908 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aArcPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
910 break;
912 case MetaActionType::PIE:
914 MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
915 const tools::Polygon aPiePoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Pie );
917 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aPiePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
919 break;
921 case MetaActionType::CHORD:
923 MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
924 const tools::Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Chord );
926 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aChordPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
928 break;
930 case MetaActionType::POLYLINE:
932 MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
933 aMtf.AddAction( new MetaPolyLineAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->GetLineInfo() ) );
935 break;
937 case MetaActionType::POLYGON:
939 MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
940 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
942 break;
944 case MetaActionType::POLYPOLYGON:
946 MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
947 aMtf.AddAction( new MetaPolyPolygonAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
949 break;
951 case MetaActionType::TEXT:
953 MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
954 aMtf.AddAction( new MetaTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
955 pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
957 break;
959 case MetaActionType::TEXTARRAY:
961 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
962 aMtf.AddAction( new MetaTextArrayAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
963 pAct->GetText(), pAct->GetDXArray(), pAct->GetIndex(), pAct->GetLen() ) );
965 break;
967 case MetaActionType::STRETCHTEXT:
969 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
970 aMtf.AddAction( new MetaStretchTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
971 pAct->GetWidth(), pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
973 break;
975 case MetaActionType::TEXTLINE:
977 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
978 aMtf.AddAction( new MetaTextLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
979 pAct->GetWidth(), pAct->GetStrikeout(), pAct->GetUnderline(), pAct->GetOverline() ) );
981 break;
983 case MetaActionType::BMPSCALE:
985 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
986 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
987 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
988 BitmapEx aBmpEx( pAct->GetBitmap() );
990 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
991 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(),
992 aBmpEx ) );
994 break;
996 case MetaActionType::BMPSCALEPART:
998 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
999 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1000 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1001 BitmapEx aBmpEx( pAct->GetBitmap() );
1003 aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
1004 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
1006 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
1008 break;
1010 case MetaActionType::BMPEXSCALE:
1012 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1013 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1014 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1015 BitmapEx aBmpEx( pAct->GetBitmapEx() );
1017 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
1019 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
1021 break;
1023 case MetaActionType::BMPEXSCALEPART:
1025 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1026 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1027 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1028 BitmapEx aBmpEx( pAct->GetBitmapEx() );
1030 aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
1031 aBmpEx.Rotate( nAngle10, COL_TRANSPARENT );
1033 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
1035 break;
1037 case MetaActionType::GRADIENT:
1039 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1041 ImplAddGradientEx( aMtf, *aMapVDev,
1042 ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ),
1043 pAct->GetGradient() );
1045 break;
1047 case MetaActionType::GRADIENTEX:
1049 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1050 aMtf.AddAction( new MetaGradientExAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1051 pAct->GetGradient() ) );
1053 break;
1055 // Handle gradientex comment block correctly
1056 case MetaActionType::COMMENT:
1058 MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction);
1059 if( pCommentAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1061 int nBeginComments( 1 );
1062 pAction = NextAction();
1064 // skip everything, except gradientex action
1065 while( pAction )
1067 const MetaActionType nType = pAction->GetType();
1069 if( MetaActionType::GRADIENTEX == nType )
1071 // Add rotated gradientex
1072 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1073 ImplAddGradientEx( aMtf, *aMapVDev,
1074 ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1075 pAct->GetGradient() );
1077 else if( MetaActionType::COMMENT == nType)
1079 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pAction);
1080 if( pAct->GetComment() == "XGRAD_SEQ_END" )
1082 // handle nested blocks
1083 --nBeginComments;
1085 // gradientex comment block: end reached, done.
1086 if( !nBeginComments )
1087 break;
1089 else if( pAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1091 // handle nested blocks
1092 ++nBeginComments;
1097 pAction =NextAction();
1100 else
1102 bool bPathStroke = (pCommentAct->GetComment() == "XPATHSTROKE_SEQ_BEGIN");
1103 if ( bPathStroke || pCommentAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1105 if ( pCommentAct->GetDataSize() )
1107 SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pCommentAct->GetData()), pCommentAct->GetDataSize(), StreamMode::READ );
1108 SvMemoryStream aDest;
1109 if ( bPathStroke )
1111 SvtGraphicStroke aStroke;
1112 ReadSvtGraphicStroke( aMemStm, aStroke );
1113 tools::Polygon aPath;
1114 aStroke.getPath( aPath );
1115 aStroke.setPath( ImplGetRotatedPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1116 WriteSvtGraphicStroke( aDest, aStroke );
1117 aMtf.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN", 0,
1118 static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1120 else
1122 SvtGraphicFill aFill;
1123 ReadSvtGraphicFill( aMemStm, aFill );
1124 tools::PolyPolygon aPath;
1125 aFill.getPath( aPath );
1126 aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1127 WriteSvtGraphicFill( aDest, aFill );
1128 aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0,
1129 static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1133 else if ( pCommentAct->GetComment() == "XPATHSTROKE_SEQ_END"
1134 || pCommentAct->GetComment() == "XPATHFILL_SEQ_END" )
1136 pAction->Execute( aMapVDev.get() );
1137 aMtf.AddAction( pAction );
1141 break;
1143 case MetaActionType::HATCH:
1145 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
1146 Hatch aHatch( pAct->GetHatch() );
1148 aHatch.SetAngle( aHatch.GetAngle() + nAngle10 );
1149 aMtf.AddAction( new MetaHatchAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1150 aHatch ) );
1152 break;
1154 case MetaActionType::Transparent:
1156 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1157 aMtf.AddAction( new MetaTransparentAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1158 pAct->GetTransparence() ) );
1160 break;
1162 case MetaActionType::FLOATTRANSPARENT:
1164 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1165 GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
1166 tools::Polygon aMtfPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1167 tools::Rectangle aMtfRect( aMtfPoly.GetBoundRect() );
1169 aTransMtf.Rotate( nAngle10 );
1170 aMtf.AddAction( new MetaFloatTransparentAction( aTransMtf, aMtfRect.TopLeft(), aMtfRect.GetSize(),
1171 pAct->GetGradient() ) );
1173 break;
1175 case MetaActionType::EPS:
1177 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
1178 GDIMetaFile aEPSMtf( pAct->GetSubstitute() );
1179 tools::Polygon aEPSPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1180 tools::Rectangle aEPSRect( aEPSPoly.GetBoundRect() );
1182 aEPSMtf.Rotate( nAngle10 );
1183 aMtf.AddAction( new MetaEPSAction( aEPSRect.TopLeft(), aEPSRect.GetSize(),
1184 pAct->GetLink(), aEPSMtf ) );
1186 break;
1188 case MetaActionType::CLIPREGION:
1190 MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1192 if( pAct->IsClipping() && pAct->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1193 aMtf.AddAction( new MetaClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( pAct->GetRegion().GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ), true ) );
1194 else
1196 aMtf.AddAction( pAction );
1199 break;
1201 case MetaActionType::ISECTRECTCLIPREGION:
1203 MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1204 aMtf.AddAction( new MetaISectRegionClipRegionAction(vcl::Region(
1205 ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor,
1206 aRotOffset, fSin, fCos )) ) );
1208 break;
1210 case MetaActionType::ISECTREGIONCLIPREGION:
1212 MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1213 const vcl::Region& rRegion = pAct->GetRegion();
1215 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() )
1216 aMtf.AddAction( new MetaISectRegionClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( rRegion.GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ) );
1217 else
1219 aMtf.AddAction( pAction );
1222 break;
1224 case MetaActionType::REFPOINT:
1226 MetaRefPointAction* pAct = static_cast<MetaRefPointAction*>(pAction);
1227 aMtf.AddAction( new MetaRefPointAction( ImplGetRotatedPoint( pAct->GetRefPoint(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->IsSetting() ) );
1229 break;
1231 case MetaActionType::FONT:
1233 MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1234 vcl::Font aFont( pAct->GetFont() );
1236 aFont.SetOrientation( aFont.GetOrientation() + nAngle10 );
1237 aMtf.AddAction( new MetaFontAction( aFont ) );
1239 break;
1241 case MetaActionType::BMP:
1242 case MetaActionType::BMPEX:
1243 case MetaActionType::MASK:
1244 case MetaActionType::MASKSCALE:
1245 case MetaActionType::MASKSCALEPART:
1246 case MetaActionType::WALLPAPER:
1247 case MetaActionType::TEXTRECT:
1248 case MetaActionType::MOVECLIPREGION:
1250 OSL_FAIL( "GDIMetaFile::Rotate(): unsupported action" );
1252 break;
1254 default:
1256 pAction->Execute( aMapVDev.get() );
1257 aMtf.AddAction( pAction );
1259 // update rotation point and offset, if necessary
1260 if( ( MetaActionType::MAPMODE == nActionType ) ||
1261 ( MetaActionType::PUSH == nActionType ) ||
1262 ( MetaActionType::POP == nActionType ) )
1264 aRotAnchor = OutputDevice::LogicToLogic( aOrigin, m_aPrefMapMode, aMapVDev->GetMapMode() );
1265 aRotOffset = OutputDevice::LogicToLogic( aOffset, m_aPrefMapMode, aMapVDev->GetMapMode() );
1268 break;
1272 aMtf.m_aPrefMapMode = m_aPrefMapMode;
1273 aMtf.m_aPrefSize = aNewBound.GetSize();
1275 *this = aMtf;
1279 static void ImplActionBounds( tools::Rectangle& o_rOutBounds,
1280 const tools::Rectangle& i_rInBounds,
1281 const std::vector<tools::Rectangle>& i_rClipStack,
1282 tools::Rectangle* o_pHairline )
1284 tools::Rectangle aBounds( i_rInBounds );
1285 if( ! i_rInBounds.IsEmpty() && ! i_rClipStack.empty() && ! i_rClipStack.back().IsEmpty() )
1286 aBounds.Intersection( i_rClipStack.back() );
1287 if( aBounds.IsEmpty() )
1288 return;
1290 if( ! o_rOutBounds.IsEmpty() )
1291 o_rOutBounds.Union( aBounds );
1292 else
1293 o_rOutBounds = aBounds;
1295 if(o_pHairline)
1297 if( ! o_pHairline->IsEmpty() )
1298 o_pHairline->Union( aBounds );
1299 else
1300 *o_pHairline = aBounds;
1304 tools::Rectangle GDIMetaFile::GetBoundRect( OutputDevice& i_rReference, tools::Rectangle* pHairline ) const
1306 ScopedVclPtrInstance< VirtualDevice > aMapVDev( i_rReference );
1308 aMapVDev->EnableOutput( false );
1309 aMapVDev->SetMapMode( GetPrefMapMode() );
1311 std::vector<tools::Rectangle> aClipStack( 1, tools::Rectangle() );
1312 std::vector<PushFlags> aPushFlagStack;
1314 tools::Rectangle aBound;
1316 if(pHairline)
1317 *pHairline = tools::Rectangle();
1319 const sal_uLong nCount(GetActionSize());
1321 for(sal_uLong a(0); a < nCount; a++)
1323 MetaAction* pAction = GetAction(a);
1324 const MetaActionType nActionType = pAction->GetType();
1325 tools::Rectangle* pUseHairline = (pHairline && aMapVDev->IsLineColor()) ? pHairline : nullptr;
1327 switch( nActionType )
1329 case MetaActionType::PIXEL:
1331 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1332 ImplActionBounds( aBound,
1333 tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1334 aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1335 aClipStack, pUseHairline );
1337 break;
1339 case MetaActionType::POINT:
1341 MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
1342 ImplActionBounds( aBound,
1343 tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1344 aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1345 aClipStack, pUseHairline );
1347 break;
1349 case MetaActionType::LINE:
1351 MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
1352 Point aP1( pAct->GetStartPoint() ), aP2( pAct->GetEndPoint() );
1353 tools::Rectangle aRect( aP1, aP2 );
1354 aRect.Justify();
1356 if(pUseHairline)
1358 const LineInfo& rLineInfo = pAct->GetLineInfo();
1360 if(0 != rLineInfo.GetWidth())
1361 pUseHairline = nullptr;
1364 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1366 break;
1368 case MetaActionType::RECT:
1370 MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
1371 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1373 break;
1375 case MetaActionType::ROUNDRECT:
1377 MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
1378 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1380 break;
1382 case MetaActionType::ELLIPSE:
1384 MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
1385 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1387 break;
1389 case MetaActionType::ARC:
1391 MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
1392 // FIXME: this is imprecise
1393 // e.g. for small arcs the whole rectangle is WAY too large
1394 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1396 break;
1398 case MetaActionType::PIE:
1400 MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
1401 // FIXME: this is imprecise
1402 // e.g. for small arcs the whole rectangle is WAY too large
1403 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1405 break;
1407 case MetaActionType::CHORD:
1409 MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
1410 // FIXME: this is imprecise
1411 // e.g. for small arcs the whole rectangle is WAY too large
1412 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1414 break;
1416 case MetaActionType::POLYLINE:
1418 MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
1419 tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1421 if(pUseHairline)
1423 const LineInfo& rLineInfo = pAct->GetLineInfo();
1425 if(0 != rLineInfo.GetWidth())
1426 pUseHairline = nullptr;
1429 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1431 break;
1433 case MetaActionType::POLYGON:
1435 MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
1436 tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1437 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1439 break;
1441 case MetaActionType::POLYPOLYGON:
1443 MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
1444 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1445 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1447 break;
1449 case MetaActionType::TEXT:
1451 MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
1452 tools::Rectangle aRect;
1453 // hdu said base = index
1454 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen() );
1455 Point aPt( pAct->GetPoint() );
1456 aRect.Move( aPt.X(), aPt.Y() );
1457 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1459 break;
1461 case MetaActionType::TEXTARRAY:
1463 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
1464 tools::Rectangle aRect;
1465 // hdu said base = index
1466 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1467 0, pAct->GetDXArray() );
1468 Point aPt( pAct->GetPoint() );
1469 aRect.Move( aPt.X(), aPt.Y() );
1470 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1472 break;
1474 case MetaActionType::STRETCHTEXT:
1476 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
1477 tools::Rectangle aRect;
1478 // hdu said base = index
1479 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1480 pAct->GetWidth() );
1481 Point aPt( pAct->GetPoint() );
1482 aRect.Move( aPt.X(), aPt.Y() );
1483 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1485 break;
1487 case MetaActionType::TEXTLINE:
1489 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
1490 // measure a test string to get ascend and descent right
1491 static constexpr OUStringLiteral pStr = u"\u00c4g";
1492 OUString aStr( pStr );
1494 tools::Rectangle aRect;
1495 aMapVDev->GetTextBoundRect( aRect, aStr, 0, 0, aStr.getLength() );
1496 Point aPt( pAct->GetStartPoint() );
1497 aRect.Move( aPt.X(), aPt.Y() );
1498 aRect.SetRight( aRect.Left() + pAct->GetWidth() );
1499 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1501 break;
1503 case MetaActionType::BMPSCALE:
1505 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1506 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1507 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1509 break;
1511 case MetaActionType::BMPSCALEPART:
1513 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1514 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1515 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1517 break;
1519 case MetaActionType::BMPEXSCALE:
1521 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1522 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1523 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1525 break;
1527 case MetaActionType::BMPEXSCALEPART:
1529 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1530 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1531 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1533 break;
1535 case MetaActionType::GRADIENT:
1537 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1538 tools::Rectangle aRect( pAct->GetRect() );
1539 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1541 break;
1543 case MetaActionType::GRADIENTEX:
1545 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1546 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1547 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1549 break;
1551 case MetaActionType::COMMENT:
1553 // nothing to do
1555 break;
1557 case MetaActionType::HATCH:
1559 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
1560 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1561 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1563 break;
1565 case MetaActionType::Transparent:
1567 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1568 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1569 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1571 break;
1573 case MetaActionType::FLOATTRANSPARENT:
1575 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1576 // MetaFloatTransparentAction is defined limiting its content Metafile
1577 // to its geometry definition(Point, Size), so use these directly
1578 const tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1579 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1581 break;
1583 case MetaActionType::EPS:
1585 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
1586 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1587 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1589 break;
1591 case MetaActionType::CLIPREGION:
1593 MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1594 if( pAct->IsClipping() )
1595 aClipStack.back() = OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() );
1596 else
1597 aClipStack.back() = tools::Rectangle();
1599 break;
1601 case MetaActionType::ISECTRECTCLIPREGION:
1603 MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1604 tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1605 if( aClipStack.back().IsEmpty() )
1606 aClipStack.back() = aRect;
1607 else
1608 aClipStack.back().Intersection( aRect );
1610 break;
1612 case MetaActionType::ISECTREGIONCLIPREGION:
1614 MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1615 tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1616 if( aClipStack.back().IsEmpty() )
1617 aClipStack.back() = aRect;
1618 else
1619 aClipStack.back().Intersection( aRect );
1621 break;
1623 case MetaActionType::BMP:
1625 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
1626 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1627 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1629 break;
1631 case MetaActionType::BMPEX:
1633 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
1634 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmapEx().GetSizePixel() ) );
1635 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1637 break;
1639 case MetaActionType::MASK:
1641 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
1642 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1643 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1645 break;
1647 case MetaActionType::MASKSCALE:
1649 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1650 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1651 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1653 break;
1655 case MetaActionType::MASKSCALEPART:
1657 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1658 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1659 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1661 break;
1663 case MetaActionType::WALLPAPER:
1665 MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
1666 tools::Rectangle aRect( pAct->GetRect() );
1667 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1669 break;
1671 case MetaActionType::TEXTRECT:
1673 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pAction);
1674 tools::Rectangle aRect( pAct->GetRect() );
1675 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1677 break;
1679 case MetaActionType::MOVECLIPREGION:
1681 MetaMoveClipRegionAction* pAct = static_cast<MetaMoveClipRegionAction*>(pAction);
1682 if( ! aClipStack.back().IsEmpty() )
1684 Size aDelta( pAct->GetHorzMove(), pAct->GetVertMove() );
1685 aDelta = OutputDevice::LogicToLogic( aDelta, aMapVDev->GetMapMode(), GetPrefMapMode() );
1686 aClipStack.back().Move( aDelta.Width(), aDelta.Width() );
1689 break;
1691 default:
1693 pAction->Execute( aMapVDev.get() );
1695 if( nActionType == MetaActionType::PUSH )
1697 MetaPushAction* pAct = static_cast<MetaPushAction*>(pAction);
1698 aPushFlagStack.push_back( pAct->GetFlags() );
1699 if( aPushFlagStack.back() & PushFlags::CLIPREGION )
1701 tools::Rectangle aRect( aClipStack.back() );
1702 aClipStack.push_back( aRect );
1705 else if( nActionType == MetaActionType::POP )
1707 // sanity check
1708 if( ! aPushFlagStack.empty() )
1710 if( aPushFlagStack.back() & PushFlags::CLIPREGION )
1712 if( aClipStack.size() > 1 )
1713 aClipStack.pop_back();
1715 aPushFlagStack.pop_back();
1719 break;
1722 return aBound;
1725 Color GDIMetaFile::ImplColAdjustFnc( const Color& rColor, const void* pColParam )
1727 return Color( ColorAlpha, rColor.GetAlpha(),
1728 static_cast<const ImplColAdjustParam*>(pColParam)->pMapR[ rColor.GetRed() ],
1729 static_cast<const ImplColAdjustParam*>(pColParam)->pMapG[ rColor.GetGreen() ],
1730 static_cast<const ImplColAdjustParam*>(pColParam)->pMapB[ rColor.GetBlue() ] );
1734 BitmapEx GDIMetaFile::ImplBmpAdjustFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1736 const ImplBmpAdjustParam* p = static_cast<const ImplBmpAdjustParam*>(pBmpParam);
1737 BitmapEx aRet( rBmpEx );
1739 aRet.Adjust( p->nLuminancePercent, p->nContrastPercent,
1740 p->nChannelRPercent, p->nChannelGPercent, p->nChannelBPercent,
1741 p->fGamma, p->bInvert );
1743 return aRet;
1746 Color GDIMetaFile::ImplColConvertFnc( const Color& rColor, const void* pColParam )
1748 sal_uInt8 cLum = rColor.GetLuminance();
1750 if( MtfConversion::N1BitThreshold == static_cast<const ImplColConvertParam*>(pColParam)->eConversion )
1751 cLum = ( cLum < 128 ) ? 0 : 255;
1753 return Color( ColorTransparency, 255 - rColor.GetAlpha(), cLum, cLum, cLum );
1756 BitmapEx GDIMetaFile::ImplBmpConvertFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1758 BitmapEx aRet( rBmpEx );
1760 aRet.Convert( static_cast<const ImplBmpConvertParam*>(pBmpParam)->eConversion );
1762 return aRet;
1765 Color GDIMetaFile::ImplColMonoFnc( const Color&, const void* pColParam )
1767 return static_cast<const ImplColMonoParam*>(pColParam)->aColor;
1770 BitmapEx GDIMetaFile::ImplBmpMonoFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1772 BitmapPalette aPal( 3 );
1773 aPal[ 0 ] = COL_BLACK;
1774 aPal[ 1 ] = COL_WHITE;
1775 aPal[ 2 ] = static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor;
1777 Bitmap aBmp(rBmpEx.GetSizePixel(), vcl::PixelFormat::N8_BPP, &aPal);
1778 aBmp.Erase( static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor );
1780 if( rBmpEx.IsAlpha() )
1781 return BitmapEx( aBmp, rBmpEx.GetAlpha() );
1782 else
1783 return BitmapEx( aBmp );
1786 Color GDIMetaFile::ImplColReplaceFnc( const Color& rColor, const void* pColParam )
1788 const sal_uLong nR = rColor.GetRed(), nG = rColor.GetGreen(), nB = rColor.GetBlue();
1790 for( sal_uLong i = 0; i < static_cast<const ImplColReplaceParam*>(pColParam)->nCount; i++ )
1792 if( ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinR[ i ] <= nR ) &&
1793 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxR[ i ] >= nR ) &&
1794 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinG[ i ] <= nG ) &&
1795 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxG[ i ] >= nG ) &&
1796 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinB[ i ] <= nB ) &&
1797 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxB[ i ] >= nB ) )
1799 return static_cast<const ImplColReplaceParam*>(pColParam)->pDstCols[ i ];
1803 return rColor;
1806 BitmapEx GDIMetaFile::ImplBmpReplaceFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1808 const ImplBmpReplaceParam* p = static_cast<const ImplBmpReplaceParam*>(pBmpParam);
1809 BitmapEx aRet( rBmpEx );
1811 aRet.Replace( p->pSrcCols, p->pDstCols, p->nCount );
1813 return aRet;
1816 void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pColParam,
1817 BmpExchangeFnc pFncBmp, const void* pBmpParam )
1819 GDIMetaFile aMtf;
1821 aMtf.m_aPrefSize = m_aPrefSize;
1822 aMtf.m_aPrefMapMode = m_aPrefMapMode;
1823 aMtf.m_bUseCanvas = m_bUseCanvas;
1825 for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
1827 const MetaActionType nType = pAction->GetType();
1829 switch( nType )
1831 case MetaActionType::PIXEL:
1833 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1834 aMtf.push_back( new MetaPixelAction( pAct->GetPoint(), pFncCol( pAct->GetColor(), pColParam ) ) );
1836 break;
1838 case MetaActionType::LINECOLOR:
1840 MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction);
1842 if( pAct->IsSetting() )
1843 pAct = new MetaLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1845 aMtf.push_back( pAct );
1847 break;
1849 case MetaActionType::FILLCOLOR:
1851 MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction);
1853 if( pAct->IsSetting() )
1854 pAct = new MetaFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1856 aMtf.push_back( pAct );
1858 break;
1860 case MetaActionType::TEXTCOLOR:
1862 MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction);
1863 aMtf.push_back( new MetaTextColorAction( pFncCol( pAct->GetColor(), pColParam ) ) );
1865 break;
1867 case MetaActionType::TEXTFILLCOLOR:
1869 MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction);
1871 if( pAct->IsSetting() )
1872 pAct = new MetaTextFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1874 aMtf.push_back( pAct );
1876 break;
1878 case MetaActionType::TEXTLINECOLOR:
1880 MetaTextLineColorAction* pAct = static_cast<MetaTextLineColorAction*>(pAction);
1882 if( pAct->IsSetting() )
1883 pAct = new MetaTextLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1885 aMtf.push_back( pAct );
1887 break;
1889 case MetaActionType::OVERLINECOLOR:
1891 MetaOverlineColorAction* pAct = static_cast<MetaOverlineColorAction*>(pAction);
1893 if( pAct->IsSetting() )
1894 pAct = new MetaOverlineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1896 aMtf.push_back( pAct );
1898 break;
1900 case MetaActionType::FONT:
1902 MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1903 vcl::Font aFont( pAct->GetFont() );
1905 aFont.SetColor( pFncCol( aFont.GetColor(), pColParam ) );
1906 aFont.SetFillColor( pFncCol( aFont.GetFillColor(), pColParam ) );
1907 aMtf.push_back( new MetaFontAction( aFont ) );
1909 break;
1911 case MetaActionType::WALLPAPER:
1913 MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
1914 Wallpaper aWall( pAct->GetWallpaper() );
1915 const tools::Rectangle& rRect = pAct->GetRect();
1917 aWall.SetColor( pFncCol( aWall.GetColor(), pColParam ) );
1919 if( aWall.IsBitmap() )
1920 aWall.SetBitmap( pFncBmp( aWall.GetBitmap(), pBmpParam ) );
1922 if( aWall.IsGradient() )
1924 Gradient aGradient( aWall.GetGradient() );
1926 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
1927 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
1928 aWall.SetGradient( aGradient );
1931 aMtf.push_back( new MetaWallpaperAction( rRect, aWall ) );
1933 break;
1935 case MetaActionType::BMP:
1936 case MetaActionType::BMPEX:
1937 case MetaActionType::MASK:
1939 OSL_FAIL( "Don't use bitmap actions of this type in metafiles!" );
1941 break;
1943 case MetaActionType::BMPSCALE:
1945 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1946 aMtf.push_back( new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(),
1947 pFncBmp( BitmapEx(pAct->GetBitmap()), pBmpParam ).GetBitmap() ) );
1949 break;
1951 case MetaActionType::BMPSCALEPART:
1953 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1954 aMtf.push_back( new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1955 pAct->GetSrcPoint(), pAct->GetSrcSize(),
1956 pFncBmp( BitmapEx(pAct->GetBitmap()), pBmpParam ).GetBitmap() )
1959 break;
1961 case MetaActionType::BMPEXSCALE:
1963 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1964 aMtf.push_back( new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(),
1965 pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
1968 break;
1970 case MetaActionType::BMPEXSCALEPART:
1972 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1973 aMtf.push_back( new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1974 pAct->GetSrcPoint(), pAct->GetSrcSize(),
1975 pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
1978 break;
1980 case MetaActionType::MASKSCALE:
1982 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
1983 aMtf.push_back( new MetaMaskScaleAction( pAct->GetPoint(), pAct->GetSize(),
1984 pAct->GetBitmap(),
1985 pFncCol( pAct->GetColor(), pColParam ) )
1988 break;
1990 case MetaActionType::MASKSCALEPART:
1992 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1993 aMtf.push_back( new MetaMaskScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1994 pAct->GetSrcPoint(), pAct->GetSrcSize(),
1995 pAct->GetBitmap(),
1996 pFncCol( pAct->GetColor(), pColParam ) )
1999 break;
2001 case MetaActionType::GRADIENT:
2003 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
2004 Gradient aGradient( pAct->GetGradient() );
2006 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
2007 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
2008 aMtf.push_back( new MetaGradientAction( pAct->GetRect(), aGradient ) );
2010 break;
2012 case MetaActionType::GRADIENTEX:
2014 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
2015 Gradient aGradient( pAct->GetGradient() );
2017 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
2018 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
2019 aMtf.push_back( new MetaGradientExAction( pAct->GetPolyPolygon(), aGradient ) );
2021 break;
2023 case MetaActionType::HATCH:
2025 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
2026 Hatch aHatch( pAct->GetHatch() );
2028 aHatch.SetColor( pFncCol( aHatch.GetColor(), pColParam ) );
2029 aMtf.push_back( new MetaHatchAction( pAct->GetPolyPolygon(), aHatch ) );
2031 break;
2033 case MetaActionType::FLOATTRANSPARENT:
2035 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
2036 GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
2038 aTransMtf.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2039 aMtf.push_back( new MetaFloatTransparentAction( aTransMtf,
2040 pAct->GetPoint(), pAct->GetSize(),
2041 pAct->GetGradient() )
2044 break;
2046 case MetaActionType::EPS:
2048 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
2049 GDIMetaFile aSubst( pAct->GetSubstitute() );
2051 aSubst.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2052 aMtf.push_back( new MetaEPSAction( pAct->GetPoint(), pAct->GetSize(),
2053 pAct->GetLink(), aSubst )
2056 break;
2058 default:
2060 aMtf.push_back( pAction );
2062 break;
2066 *this = aMtf;
2069 void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
2070 short nChannelRPercent, short nChannelGPercent,
2071 short nChannelBPercent, double fGamma, bool bInvert, bool msoBrightness )
2073 // nothing to do? => return quickly
2074 if( !(nLuminancePercent || nContrastPercent ||
2075 nChannelRPercent || nChannelGPercent || nChannelBPercent ||
2076 ( fGamma != 1.0 ) || bInvert) )
2077 return;
2079 double fM, fROff, fGOff, fBOff, fOff;
2080 ImplColAdjustParam aColParam;
2081 ImplBmpAdjustParam aBmpParam;
2083 aColParam.pMapR.reset(new sal_uInt8[ 256 ]);
2084 aColParam.pMapG.reset(new sal_uInt8[ 256 ]);
2085 aColParam.pMapB.reset(new sal_uInt8[ 256 ]);
2087 // calculate slope
2088 if( nContrastPercent >= 0 )
2089 fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0, 100 ) );
2090 else
2091 fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100, 0 ) ) / 128.0;
2093 if(!msoBrightness)
2094 // total offset = luminance offset + contrast offset
2095 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55 + 128.0 - fM * 128.0;
2096 else
2097 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55;
2099 // channel offset = channel offset + total offset
2100 fROff = nChannelRPercent * 2.55 + fOff;
2101 fGOff = nChannelGPercent * 2.55 + fOff;
2102 fBOff = nChannelBPercent * 2.55 + fOff;
2104 // calculate gamma value
2105 fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
2106 const bool bGamma = ( fGamma != 1.0 );
2108 // create mapping table
2109 for( tools::Long nX = 0; nX < 256; nX++ )
2111 if(!msoBrightness)
2113 aColParam.pMapR[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fROff ), 0, 255 ));
2114 aColParam.pMapG[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fGOff ), 0, 255 ));
2115 aColParam.pMapB[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( nX * fM + fBOff ), 0, 255 ));
2117 else
2119 aColParam.pMapR[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fROff/2-128) * fM + 128 + fROff/2 ), 0, 255 ));
2120 aColParam.pMapG[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fGOff/2-128) * fM + 128 + fGOff/2 ), 0, 255 ));
2121 aColParam.pMapB[ nX ] = static_cast<sal_uInt8>(MinMax( FRound( (nX+fBOff/2-128) * fM + 128 + fBOff/2 ), 0, 255 ));
2123 if( bGamma )
2125 aColParam.pMapR[ nX ] = GAMMA( aColParam.pMapR[ nX ], fGamma );
2126 aColParam.pMapG[ nX ] = GAMMA( aColParam.pMapG[ nX ], fGamma );
2127 aColParam.pMapB[ nX ] = GAMMA( aColParam.pMapB[ nX ], fGamma );
2130 if( bInvert )
2132 aColParam.pMapR[ nX ] = ~aColParam.pMapR[ nX ];
2133 aColParam.pMapG[ nX ] = ~aColParam.pMapG[ nX ];
2134 aColParam.pMapB[ nX ] = ~aColParam.pMapB[ nX ];
2138 aBmpParam.nLuminancePercent = nLuminancePercent;
2139 aBmpParam.nContrastPercent = nContrastPercent;
2140 aBmpParam.nChannelRPercent = nChannelRPercent;
2141 aBmpParam.nChannelGPercent = nChannelGPercent;
2142 aBmpParam.nChannelBPercent = nChannelBPercent;
2143 aBmpParam.fGamma = fGamma;
2144 aBmpParam.bInvert = bInvert;
2146 // do color adjustment
2147 ImplExchangeColors( ImplColAdjustFnc, &aColParam, ImplBmpAdjustFnc, &aBmpParam );
2150 void GDIMetaFile::Convert( MtfConversion eConversion )
2152 ImplColConvertParam aColParam;
2153 ImplBmpConvertParam aBmpParam;
2155 aColParam.eConversion = eConversion;
2156 aBmpParam.eConversion = ( MtfConversion::N1BitThreshold == eConversion ) ? BmpConversion::N1BitThreshold : BmpConversion::N8BitGreys;
2158 ImplExchangeColors( ImplColConvertFnc, &aColParam, ImplBmpConvertFnc, &aBmpParam );
2161 void GDIMetaFile::ReplaceColors( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount )
2163 ImplColReplaceParam aColParam;
2164 ImplBmpReplaceParam aBmpParam;
2166 aColParam.pMinR.reset(new sal_uLong[ nColorCount ]);
2167 aColParam.pMaxR.reset(new sal_uLong[ nColorCount ]);
2168 aColParam.pMinG.reset(new sal_uLong[ nColorCount ]);
2169 aColParam.pMaxG.reset(new sal_uLong[ nColorCount ]);
2170 aColParam.pMinB.reset(new sal_uLong[ nColorCount ]);
2171 aColParam.pMaxB.reset(new sal_uLong[ nColorCount ]);
2173 for( sal_uLong i = 0; i < nColorCount; i++ )
2175 tools::Long nVal;
2177 nVal = pSearchColors[ i ].GetRed();
2178 aColParam.pMinR[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
2179 aColParam.pMaxR[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
2181 nVal = pSearchColors[ i ].GetGreen();
2182 aColParam.pMinG[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
2183 aColParam.pMaxG[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
2185 nVal = pSearchColors[ i ].GetBlue();
2186 aColParam.pMinB[ i ] = static_cast<sal_uLong>(std::max( nVal, tools::Long(0) ));
2187 aColParam.pMaxB[ i ] = static_cast<sal_uLong>(std::min( nVal, tools::Long(255) ));
2190 aColParam.pDstCols = pReplaceColors;
2191 aColParam.nCount = nColorCount;
2193 aBmpParam.pSrcCols = pSearchColors;
2194 aBmpParam.pDstCols = pReplaceColors;
2195 aBmpParam.nCount = nColorCount;
2197 ImplExchangeColors( ImplColReplaceFnc, &aColParam, ImplBmpReplaceFnc, &aBmpParam );
2200 GDIMetaFile GDIMetaFile::GetMonochromeMtf( const Color& rColor ) const
2202 GDIMetaFile aRet( *this );
2204 ImplColMonoParam aColParam;
2205 ImplBmpMonoParam aBmpParam;
2207 aColParam.aColor = rColor;
2208 aBmpParam.aColor = rColor;
2210 aRet.ImplExchangeColors( ImplColMonoFnc, &aColParam, ImplBmpMonoFnc, &aBmpParam );
2212 return aRet;
2215 BitmapChecksum GDIMetaFile::GetChecksum() const
2217 SvMemoryStream aMemStm( 65535, 65535 );
2218 ImplMetaWriteData aWriteData;
2219 SVBT16 aBT16;
2220 SVBT32 aBT32;
2221 BitmapChecksumOctetArray aBCOA;
2222 BitmapChecksum nCrc = 0;
2224 aWriteData.meActualCharSet = aMemStm.GetStreamCharSet();
2225 for( size_t i = 0, nObjCount = GetActionSize(); i < nObjCount; i++ )
2227 MetaAction* pAction = GetAction( i );
2229 switch( pAction->GetType() )
2231 case MetaActionType::BMP:
2233 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(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 break;
2249 case MetaActionType::BMPSCALE:
2251 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
2253 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2254 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2256 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2257 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2259 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2260 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2262 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2263 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2265 Int32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2266 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2268 Int32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2269 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2271 break;
2273 case MetaActionType::BMPSCALEPART:
2275 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
2277 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2278 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2280 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2281 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2283 Int32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2284 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2286 Int32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2287 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2289 Int32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2290 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2292 Int32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2293 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2295 Int32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2296 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2298 Int32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2299 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2301 Int32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2302 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2304 Int32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2305 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2307 break;
2309 case MetaActionType::BMPEX:
2311 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(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 break;
2327 case MetaActionType::BMPEXSCALE:
2329 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
2331 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2332 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2334 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2335 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2337 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2338 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2340 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2341 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2343 Int32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2344 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2346 Int32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2347 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2349 break;
2351 case MetaActionType::BMPEXSCALEPART:
2353 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
2355 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2356 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2358 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2359 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2361 Int32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2362 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2364 Int32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2365 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2367 Int32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2368 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2370 Int32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2371 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2373 Int32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2374 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2376 Int32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2377 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2379 Int32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2380 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2382 Int32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2383 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2385 break;
2387 case MetaActionType::MASK:
2389 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
2391 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2392 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2394 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2395 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2397 UInt32ToSVBT32( sal_uInt32(pAct->GetColor()), aBT32 );
2398 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2400 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2401 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2403 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2404 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2406 break;
2408 case MetaActionType::MASKSCALE:
2410 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
2412 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2413 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2415 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2416 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2418 UInt32ToSVBT32( sal_uInt32(pAct->GetColor()), aBT32 );
2419 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2421 Int32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2422 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2424 Int32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2425 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2427 Int32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2428 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2430 Int32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2431 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2433 break;
2435 case MetaActionType::MASKSCALEPART:
2437 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
2439 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2440 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2442 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2443 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2445 UInt32ToSVBT32( sal_uInt32(pAct->GetColor()), aBT32 );
2446 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2448 Int32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2449 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2451 Int32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2452 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2454 Int32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2455 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2457 Int32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2458 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2460 Int32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2461 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2463 Int32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2464 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2466 Int32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2467 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2469 Int32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2470 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2472 break;
2474 case MetaActionType::EPS :
2476 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
2477 nCrc = vcl_get_checksum( nCrc, pAct->GetLink().GetData(), pAct->GetLink().GetDataSize() );
2479 break;
2481 case MetaActionType::CLIPREGION :
2483 MetaClipRegionAction& rAct = static_cast<MetaClipRegionAction&>(*pAction);
2484 const vcl::Region& rRegion = rAct.GetRegion();
2486 if(rRegion.HasPolyPolygonOrB2DPolyPolygon())
2488 // It has shown that this is a possible bottleneck for checksum calculation.
2489 // In worst case a very expensive RegionHandle representation gets created.
2490 // In this case it's cheaper to use the PolyPolygon
2491 const basegfx::B2DPolyPolygon aPolyPolygon(rRegion.GetAsB2DPolyPolygon());
2492 SVBT64 aSVBT64;
2494 for(auto const& rPolygon : aPolyPolygon)
2496 const sal_uInt32 nPointCount(rPolygon.count());
2497 const bool bControl(rPolygon.areControlPointsUsed());
2499 for(sal_uInt32 b(0); b < nPointCount; b++)
2501 const basegfx::B2DPoint aPoint(rPolygon.getB2DPoint(b));
2503 DoubleToSVBT64(aPoint.getX(), aSVBT64);
2504 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2505 DoubleToSVBT64(aPoint.getY(), aSVBT64);
2506 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2508 if(bControl)
2510 if(rPolygon.isPrevControlPointUsed(b))
2512 const basegfx::B2DPoint aCtrl(rPolygon.getPrevControlPoint(b));
2514 DoubleToSVBT64(aCtrl.getX(), aSVBT64);
2515 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2516 DoubleToSVBT64(aCtrl.getY(), aSVBT64);
2517 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2520 if(rPolygon.isNextControlPointUsed(b))
2522 const basegfx::B2DPoint aCtrl(rPolygon.getNextControlPoint(b));
2524 DoubleToSVBT64(aCtrl.getX(), aSVBT64);
2525 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2526 DoubleToSVBT64(aCtrl.getY(), aSVBT64);
2527 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2533 sal_uInt8 tmp = static_cast<sal_uInt8>(rAct.IsClipping());
2534 nCrc = vcl_get_checksum(nCrc, &tmp, 1);
2536 else
2538 pAction->Write( aMemStm, &aWriteData );
2539 nCrc = vcl_get_checksum( nCrc, aMemStm.GetData(), aMemStm.Tell() );
2540 aMemStm.Seek( 0 );
2543 break;
2545 default:
2547 pAction->Write( aMemStm, &aWriteData );
2548 nCrc = vcl_get_checksum( nCrc, aMemStm.GetData(), aMemStm.Tell() );
2549 aMemStm.Seek( 0 );
2551 break;
2555 return nCrc;
2558 sal_uLong GDIMetaFile::GetSizeBytes() const
2560 sal_uLong nSizeBytes = 0;
2562 for( size_t i = 0, nObjCount = GetActionSize(); i < nObjCount; ++i )
2564 MetaAction* pAction = GetAction( i );
2566 // default action size is set to 32 (=> not the exact value)
2567 nSizeBytes += 32;
2569 // add sizes for large action content
2570 switch( pAction->GetType() )
2572 case MetaActionType::BMP: nSizeBytes += static_cast<MetaBmpAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2573 case MetaActionType::BMPSCALE: nSizeBytes += static_cast<MetaBmpScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2574 case MetaActionType::BMPSCALEPART: nSizeBytes += static_cast<MetaBmpScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2576 case MetaActionType::BMPEX: nSizeBytes += static_cast<MetaBmpExAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2577 case MetaActionType::BMPEXSCALE: nSizeBytes += static_cast<MetaBmpExScaleAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2578 case MetaActionType::BMPEXSCALEPART: nSizeBytes += static_cast<MetaBmpExScalePartAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2580 case MetaActionType::MASK: nSizeBytes += static_cast<MetaMaskAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2581 case MetaActionType::MASKSCALE: nSizeBytes += static_cast<MetaMaskScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2582 case MetaActionType::MASKSCALEPART: nSizeBytes += static_cast<MetaMaskScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2584 case MetaActionType::POLYLINE: nSizeBytes += static_cast<MetaPolyLineAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2585 case MetaActionType::POLYGON: nSizeBytes += static_cast<MetaPolygonAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2586 case MetaActionType::POLYPOLYGON:
2588 const tools::PolyPolygon& rPolyPoly = static_cast<MetaPolyPolygonAction*>( pAction )->GetPolyPolygon();
2590 for( sal_uInt16 n = 0; n < rPolyPoly.Count(); ++n )
2591 nSizeBytes += ( rPolyPoly[ n ].GetSize() * sizeof( Point ) );
2593 break;
2595 case MetaActionType::TEXT: nSizeBytes += static_cast<MetaTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2596 case MetaActionType::STRETCHTEXT: nSizeBytes += static_cast<MetaStretchTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2597 case MetaActionType::TEXTRECT: nSizeBytes += static_cast<MetaTextRectAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2598 case MetaActionType::TEXTARRAY:
2600 MetaTextArrayAction* pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
2602 nSizeBytes += ( pTextArrayAction->GetText().getLength() * sizeof( sal_Unicode ) );
2604 if( pTextArrayAction->GetDXArray() )
2605 nSizeBytes += ( pTextArrayAction->GetLen() << 2 );
2607 break;
2608 default: break;
2612 return nSizeBytes;
2615 namespace
2617 class DepthGuard
2619 private:
2620 ImplMetaReadData& m_rData;
2621 rtl_TextEncoding m_eOrigCharSet;
2622 public:
2623 DepthGuard(ImplMetaReadData& rData, SvStream const & rIStm)
2624 : m_rData(rData)
2625 , m_eOrigCharSet(m_rData.meActualCharSet)
2627 ++m_rData.mnParseDepth;
2628 m_rData.meActualCharSet = rIStm.GetStreamCharSet();
2630 bool TooDeep() const { return m_rData.mnParseDepth > 1024; }
2631 ~DepthGuard()
2633 --m_rData.mnParseDepth;
2634 m_rData.meActualCharSet = m_eOrigCharSet;
2639 SvStream& ReadGDIMetaFile(SvStream& rIStm, GDIMetaFile& rGDIMetaFile, ImplMetaReadData* pData)
2641 if (rIStm.GetError())
2643 SAL_WARN("vcl.gdi", "Stream error: " << rIStm.GetError());
2644 return rIStm;
2647 sal_uLong nStmPos = rIStm.Tell();
2648 SvStreamEndian nOldFormat = rIStm.GetEndian();
2650 rIStm.SetEndian( SvStreamEndian::LITTLE );
2654 char aId[7];
2655 aId[0] = 0;
2656 aId[6] = 0;
2657 rIStm.ReadBytes( aId, 6 );
2659 if ( !strcmp( aId, "VCLMTF" ) )
2661 // new format
2662 sal_uInt32 nStmCompressMode = 0;
2663 sal_uInt32 nCount = 0;
2664 std::unique_ptr<VersionCompatRead> pCompat(new VersionCompatRead(rIStm));
2666 rIStm.ReadUInt32( nStmCompressMode );
2667 TypeSerializer aSerializer(rIStm);
2668 aSerializer.readMapMode(rGDIMetaFile.m_aPrefMapMode);
2669 aSerializer.readSize(rGDIMetaFile.m_aPrefSize);
2670 rIStm.ReadUInt32( nCount );
2672 pCompat.reset(); // destructor writes stuff into the header
2674 std::unique_ptr<ImplMetaReadData> xReadData;
2675 if (!pData)
2677 xReadData.reset(new ImplMetaReadData);
2678 pData = xReadData.get();
2680 DepthGuard aDepthGuard(*pData, rIStm);
2682 if (aDepthGuard.TooDeep())
2683 throw std::runtime_error("too much recursion");
2685 for( sal_uInt32 nAction = 0; ( nAction < nCount ) && !rIStm.eof(); nAction++ )
2687 rtl::Reference<MetaAction> pAction = MetaAction::ReadMetaAction(rIStm, pData);
2688 if( pAction )
2690 if (pAction->GetType() == MetaActionType::COMMENT)
2692 MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction.get());
2693 if ( pCommentAct->GetComment() == "EMF_PLUS" )
2694 rGDIMetaFile.UseCanvas( true );
2696 rGDIMetaFile.AddAction( pAction );
2700 else
2702 rIStm.Seek( nStmPos );
2703 SVMConverter( rIStm, rGDIMetaFile );
2706 catch (...)
2708 SAL_WARN("vcl", "GDIMetaFile exception during load");
2709 rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
2712 // check for errors
2713 if( rIStm.GetError() )
2715 rGDIMetaFile.Clear();
2716 rIStm.Seek( nStmPos );
2719 rIStm.SetEndian( nOldFormat );
2720 return rIStm;
2723 SvStream& WriteGDIMetaFile( SvStream& rOStm, const GDIMetaFile& rGDIMetaFile )
2725 if( !rOStm.GetError() )
2727 const_cast< GDIMetaFile& >( rGDIMetaFile ).Write( rOStm );
2729 return rOStm;
2732 SvStream& GDIMetaFile::Read( SvStream& rIStm )
2734 Clear();
2735 ReadGDIMetaFile( rIStm, *this );
2737 return rIStm;
2740 SvStream& GDIMetaFile::Write( SvStream& rOStm )
2742 const SvStreamCompressFlags nStmCompressMode = rOStm.GetCompressMode();
2743 SvStreamEndian nOldFormat = rOStm.GetEndian();
2745 rOStm.SetEndian( SvStreamEndian::LITTLE );
2746 rOStm.WriteBytes( "VCLMTF", 6 );
2749 VersionCompatWrite aCompat(rOStm, 1);
2751 rOStm.WriteUInt32(static_cast<sal_uInt32>(nStmCompressMode));
2752 TypeSerializer aSerializer(rOStm);
2753 aSerializer.writeMapMode(m_aPrefMapMode);
2754 aSerializer.writeSize(m_aPrefSize);
2755 rOStm.WriteUInt32(GetActionSize());
2756 } // VersionCompatWrite dtor writes stuff into the header
2758 ImplMetaWriteData aWriteData;
2760 aWriteData.meActualCharSet = rOStm.GetStreamCharSet();
2762 MetaAction* pAct = FirstAction();
2763 while ( pAct )
2765 pAct->Write( rOStm, &aWriteData );
2766 pAct = NextAction();
2769 rOStm.SetEndian( nOldFormat );
2771 return rOStm;
2774 bool GDIMetaFile::CreateThumbnail(BitmapEx& rBitmapEx, BmpConversion eColorConversion, BmpScaleFlag nScaleFlag) const
2776 // initialization seems to be complicated but is used to avoid rounding errors
2777 ScopedVclPtrInstance< VirtualDevice > aVDev;
2778 // set Enable to tease the rendering down the code paths which use B2DPolygon and
2779 // avoid integer overflows on scaling tools::Polygon, e.g. moz1545040-1.svg
2780 // note: this is similar to DocumentToGraphicRenderer::renderToGraphic
2781 aVDev->SetAntialiasing(AntialiasingFlags::Enable | aVDev->GetAntialiasing());
2782 const Point aNullPt;
2783 const Point aTLPix( aVDev->LogicToPixel( aNullPt, GetPrefMapMode() ) );
2784 const Point aBRPix( aVDev->LogicToPixel( Point( GetPrefSize().Width() - 1, GetPrefSize().Height() - 1 ), GetPrefMapMode() ) );
2785 Size aDrawSize( aVDev->LogicToPixel( GetPrefSize(), GetPrefMapMode() ) );
2786 Size aSizePix( std::abs( aBRPix.X() - aTLPix.X() ) + 1, std::abs( aBRPix.Y() - aTLPix.Y() ) + 1 );
2787 sal_uInt32 nMaximumExtent = 256;
2789 if (!rBitmapEx.IsEmpty())
2790 rBitmapEx.SetEmpty();
2792 // determine size that has the same aspect ratio as image size and
2793 // fits into the rectangle determined by nMaximumExtent
2794 if ( aSizePix.Width() && aSizePix.Height()
2795 && ( sal::static_int_cast< tools::ULong >(aSizePix.Width()) >
2796 nMaximumExtent ||
2797 sal::static_int_cast< tools::ULong >(aSizePix.Height()) >
2798 nMaximumExtent ) )
2800 const Size aOldSizePix( aSizePix );
2801 double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height();
2803 if ( fWH <= 1.0 )
2805 aSizePix.setWidth( FRound( nMaximumExtent * fWH ) );
2806 aSizePix.setHeight( nMaximumExtent );
2808 else
2810 aSizePix.setWidth( nMaximumExtent );
2811 aSizePix.setHeight( FRound( nMaximumExtent / fWH ) );
2814 aDrawSize.setWidth( FRound( ( static_cast< double >( aDrawSize.Width() ) * aSizePix.Width() ) / aOldSizePix.Width() ) );
2815 aDrawSize.setHeight( FRound( ( static_cast< double >( aDrawSize.Height() ) * aSizePix.Height() ) / aOldSizePix.Height() ) );
2818 // draw image(s) into VDev and get resulting image
2819 // do it 4x larger to be able to scale it down & get beautiful antialias
2820 Size aAntialiasSize(aSizePix.Width() * 4, aSizePix.Height() * 4);
2821 if (aVDev->SetOutputSizePixel(aAntialiasSize))
2823 // antialias: provide 4x larger size, and then scale down the result
2824 Size aAntialias(aDrawSize.Width() * 4, aDrawSize.Height() * 4);
2826 // draw metafile into VDev
2827 const_cast<GDIMetaFile *>(this)->WindStart();
2828 const_cast<GDIMetaFile *>(this)->Play(*aVDev, Point(), aAntialias);
2830 // get paint bitmap
2831 BitmapEx aBitmap( aVDev->GetBitmapEx( aNullPt, aVDev->GetOutputSizePixel() ) );
2833 // scale down the image to the desired size - use the input scaler for the scaling operation
2834 aBitmap.Scale(aDrawSize, nScaleFlag);
2836 // convert to desired bitmap color format
2837 Size aSize(aBitmap.GetSizePixel());
2838 if (aSize.Width() && aSize.Height())
2839 aBitmap.Convert(eColorConversion);
2841 rBitmapEx = aBitmap;
2844 return !rBitmapEx.IsEmpty();
2847 void GDIMetaFile::UseCanvas( bool _bUseCanvas )
2849 m_bUseCanvas = _bUseCanvas;
2852 void GDIMetaFile::dumpAsXml(const char* pFileName) const
2854 SvFileStream aStream(pFileName ? OUString::fromUtf8(pFileName) : OUString("file:///tmp/metafile.xml"),
2855 StreamMode::STD_READWRITE | StreamMode::TRUNC);
2856 assert(aStream.good());
2857 MetafileXmlDump aDumper;
2858 aDumper.dump(*this, aStream);
2861 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */