Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / gdimtf.cxx
blobd339f7f5ff46d6e4c0caafe75149ba0decd359b4
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 <rtl/crc.h>
21 #include <cstdlib>
22 #include <memory>
23 #include <tools/stream.hxx>
24 #include <tools/vcompat.hxx>
25 #include <tools/fract.hxx>
26 #include <vcl/metaact.hxx>
27 #include <vcl/salbtype.hxx>
28 #include <vcl/outdev.hxx>
29 #include <vcl/window.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/gdimtf.hxx>
33 #include <vcl/graphictools.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <vcl/canvastools.hxx>
37 #include "svmconverter.hxx"
39 #include <salbmp.hxx>
40 #include <salinst.hxx>
41 #include <svdata.hxx>
43 #include <com/sun/star/beans/XFastPropertySet.hpp>
44 #include <com/sun/star/rendering/MtfRenderer.hpp>
45 #include <com/sun/star/rendering/XBitmapCanvas.hpp>
46 #include <com/sun/star/rendering/XCanvas.hpp>
47 #include <comphelper/processfactory.hxx>
48 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
49 #include <com/sun/star/lang/XInitialization.hpp>
50 #include <com/sun/star/awt/XGraphics.hpp>
51 #include <com/sun/star/graphic/XGraphic.hpp>
52 #include <com/sun/star/graphic/XGraphicRenderer.hpp>
54 using namespace com::sun::star;
56 #define GAMMA( _def_cVal, _def_InvGamma ) ((sal_uInt8)MinMax(FRound(pow( _def_cVal/255.0,_def_InvGamma)*255.0),0,255))
58 struct ImplColAdjustParam
60 sal_uInt8* pMapR;
61 sal_uInt8* pMapG;
62 sal_uInt8* pMapB;
65 struct ImplBmpAdjustParam
67 short nLuminancePercent;
68 short nContrastPercent;
69 short nChannelRPercent;
70 short nChannelGPercent;
71 short nChannelBPercent;
72 double fGamma;
73 bool bInvert;
76 struct ImplColConvertParam
78 MtfConversion eConversion;
81 struct ImplBmpConvertParam
83 BmpConversion eConversion;
86 struct ImplColMonoParam
88 Color aColor;
91 struct ImplBmpMonoParam
93 Color aColor;
96 struct ImplColReplaceParam
98 sal_uLong* pMinR;
99 sal_uLong* pMaxR;
100 sal_uLong* pMinG;
101 sal_uLong* pMaxG;
102 sal_uLong* pMinB;
103 sal_uLong* pMaxB;
104 const Color* pDstCols;
105 sal_uLong nCount;
108 struct ImplBmpReplaceParam
110 const Color* pSrcCols;
111 const Color* pDstCols;
112 sal_uLong nCount;
113 const sal_uLong* pTols;
116 GDIMetaFile::GDIMetaFile() :
117 m_nCurrentActionElement( 0 ),
118 m_aPrefSize ( 1, 1 ),
119 m_pPrev ( nullptr ),
120 m_pNext ( nullptr ),
121 m_pOutDev ( nullptr ),
122 m_bPause ( false ),
123 m_bRecord ( false ),
124 m_bUseCanvas ( false )
128 GDIMetaFile::GDIMetaFile( const GDIMetaFile& rMtf ) :
129 m_nCurrentActionElement( rMtf.m_nCurrentActionElement ),
130 m_aPrefMapMode ( rMtf.m_aPrefMapMode ),
131 m_aPrefSize ( rMtf.m_aPrefSize ),
132 m_pPrev ( rMtf.m_pPrev ),
133 m_pNext ( rMtf.m_pNext ),
134 m_pOutDev ( nullptr ),
135 m_bPause ( false ),
136 m_bRecord ( false ),
137 m_bUseCanvas ( rMtf.m_bUseCanvas )
139 // Increment RefCount of MetaActions
140 for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
142 rMtf.GetAction( i )->Duplicate();
143 m_aList.push_back( rMtf.GetAction( i ) );
146 if( rMtf.m_bRecord )
148 Record( rMtf.m_pOutDev );
150 if ( rMtf.m_bPause )
151 Pause( true );
155 GDIMetaFile::~GDIMetaFile()
157 Clear();
160 size_t GDIMetaFile::GetActionSize() const
162 return m_aList.size();
165 MetaAction* GDIMetaFile::GetAction( size_t nAction ) const
167 return (nAction < m_aList.size()) ? m_aList[ nAction ] : nullptr;
170 MetaAction* GDIMetaFile::FirstAction()
172 m_nCurrentActionElement = 0;
173 return m_aList.empty() ? nullptr : m_aList[ 0 ];
176 MetaAction* GDIMetaFile::NextAction()
178 return ( m_nCurrentActionElement + 1 < m_aList.size() ) ? m_aList[ ++m_nCurrentActionElement ] : nullptr;
181 MetaAction* GDIMetaFile::ReplaceAction( MetaAction* pAction, size_t nAction )
183 if ( nAction >= m_aList.size() )
185 // this method takes ownership of pAction and is
186 // therefore responsible for deleting it
187 pAction->Delete();
188 return nullptr;
190 //fdo#39995 This does't increment the incoming action ref-count nor does it
191 //decrement the outgoing action ref-count
192 std::swap(pAction, m_aList[nAction]);
193 return pAction;
196 GDIMetaFile& GDIMetaFile::operator=( const GDIMetaFile& rMtf )
198 if( this != &rMtf )
200 Clear();
202 // Increment RefCount of MetaActions
203 for( size_t i = 0, n = rMtf.GetActionSize(); i < n; ++i )
205 rMtf.GetAction( i )->Duplicate();
206 m_aList.push_back( rMtf.GetAction( i ) );
209 m_aPrefMapMode = rMtf.m_aPrefMapMode;
210 m_aPrefSize = rMtf.m_aPrefSize;
211 m_pPrev = rMtf.m_pPrev;
212 m_pNext = rMtf.m_pNext;
213 m_pOutDev = nullptr;
214 m_bPause = false;
215 m_bRecord = false;
216 m_bUseCanvas = rMtf.m_bUseCanvas;
218 if( rMtf.m_bRecord )
220 Record( rMtf.m_pOutDev );
222 if( rMtf.m_bPause )
223 Pause( true );
227 return *this;
230 bool GDIMetaFile::operator==( const GDIMetaFile& rMtf ) const
232 const size_t nObjCount = m_aList.size();
233 bool bRet = false;
235 if( this == &rMtf )
236 bRet = true;
237 else if( rMtf.GetActionSize() == nObjCount &&
238 rMtf.GetPrefSize() == m_aPrefSize &&
239 rMtf.GetPrefMapMode() == m_aPrefMapMode )
241 bRet = true;
243 for( size_t n = 0; n < nObjCount; n++ )
245 if( m_aList[ n ] != rMtf.GetAction( n ) )
247 bRet = false;
248 break;
253 return bRet;
256 void GDIMetaFile::Clear()
258 if( m_bRecord )
259 Stop();
261 for(MetaAction* i : m_aList)
262 i->Delete();
263 m_aList.clear();
266 void GDIMetaFile::Linker( OutputDevice* pOut, bool bLink )
268 if( bLink )
270 m_pNext = nullptr;
271 m_pPrev = pOut->GetConnectMetaFile();
272 pOut->SetConnectMetaFile( this );
274 if( m_pPrev )
275 m_pPrev->m_pNext = this;
277 else
279 if( m_pNext )
281 m_pNext->m_pPrev = m_pPrev;
283 if( m_pPrev )
284 m_pPrev->m_pNext = m_pNext;
286 else
288 if( m_pPrev )
289 m_pPrev->m_pNext = nullptr;
291 pOut->SetConnectMetaFile( m_pPrev );
294 m_pPrev = nullptr;
295 m_pNext = nullptr;
299 void GDIMetaFile::Record( OutputDevice* pOut )
301 if( m_bRecord )
302 Stop();
304 m_nCurrentActionElement = m_aList.empty() ? 0 : (m_aList.size() - 1);
305 m_pOutDev = pOut;
306 m_bRecord = true;
307 Linker( pOut, true );
310 void GDIMetaFile::Play( GDIMetaFile& rMtf )
312 if ( !m_bRecord && !rMtf.m_bRecord )
314 MetaAction* pAction = GetCurAction();
315 const size_t nObjCount = m_aList.size();
317 rMtf.UseCanvas( rMtf.GetUseCanvas() || m_bUseCanvas );
319 for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nObjCount; nCurPos++ )
321 if( pAction )
323 pAction->Duplicate();
324 rMtf.AddAction( pAction );
327 pAction = NextAction();
332 void GDIMetaFile::Play( OutputDevice* pOut, size_t nPos )
334 if( !m_bRecord )
336 MetaAction* pAction = GetCurAction();
337 const size_t nObjCount = m_aList.size();
338 size_t nSyncCount = ( pOut->GetOutDevType() == OUTDEV_WINDOW ) ? 0x000000ff : 0xffffffff;
340 if( nPos > nObjCount )
341 nPos = nObjCount;
343 // #i23407# Set backwards-compatible text language and layout mode
344 // This is necessary, since old metafiles don't even know of these
345 // recent add-ons. Newer metafiles must of course explicitly set
346 // those states.
347 pOut->Push( PushFlags::TEXTLAYOUTMODE|PushFlags::TEXTLANGUAGE );
348 pOut->SetLayoutMode( ComplexTextLayoutFlags::Default );
349 pOut->SetDigitLanguage( LANGUAGE_SYSTEM );
351 SAL_INFO( "vcl.gdi", "GDIMetaFile::Play on device of size: " << pOut->GetOutputSizePixel().Width() << " " << pOut->GetOutputSizePixel().Height());
353 if( !ImplPlayWithRenderer( pOut, Point(0,0), pOut->GetOutputSize() ) ) {
354 size_t i = 0;
355 for( size_t nCurPos = m_nCurrentActionElement; nCurPos < nPos; nCurPos++ )
357 if( pAction )
359 pAction->Execute( pOut );
361 // flush output from time to time
362 if( i++ > nSyncCount )
364 static_cast<vcl::Window*>( pOut )->Flush();
365 i = 0;
369 pAction = NextAction();
372 pOut->Pop();
376 bool GDIMetaFile::ImplPlayWithRenderer( OutputDevice* pOut, const Point& rPos, Size rLogicDestSize )
378 if (!m_bUseCanvas)
379 return false;
381 Size rDestSize( pOut->LogicToPixel( rLogicDestSize ) );
383 const vcl::Window* win = dynamic_cast <vcl::Window*> ( pOut );
385 if (!win)
386 win = Application::GetActiveTopWindow();
387 if (!win)
388 win = Application::GetFirstTopLevelWindow();
390 if (!win)
391 return false;
395 uno::Reference<rendering::XCanvas> xCanvas = win->GetCanvas ();
397 if (!xCanvas.is())
398 return false;
400 Size aSize (rDestSize.Width () + 1, rDestSize.Height () + 1);
401 uno::Reference<rendering::XBitmap> xBitmap = xCanvas->getDevice ()->createCompatibleAlphaBitmap (vcl::unotools::integerSize2DFromSize( aSize));
402 if( xBitmap.is () )
404 uno::Reference< rendering::XBitmapCanvas > xBitmapCanvas( xBitmap, uno::UNO_QUERY );
405 if( xBitmapCanvas.is() )
407 uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
408 uno::Reference< rendering::XMtfRenderer > xMtfRenderer = rendering::MtfRenderer::createWithBitmapCanvas( xContext, xBitmapCanvas );
410 xBitmapCanvas->clear();
411 uno::Reference< beans::XFastPropertySet > xMtfFastPropertySet( xMtfRenderer, uno::UNO_QUERY );
412 if( xMtfFastPropertySet.is() )
413 // set this metafile to the renderer to
414 // speedup things (instead of copying data to
415 // sequence of bytes passed to renderer)
416 xMtfFastPropertySet->setFastPropertyValue( 0, uno::Any( reinterpret_cast<sal_Int64>( this ) ) );
418 xMtfRenderer->draw( rDestSize.Width(), rDestSize.Height() );
420 BitmapEx aBitmapEx;
421 if( aBitmapEx.Create( xBitmapCanvas, aSize ) )
423 if ( pOut->GetMapMode() == MapUnit::MapPixel )
424 pOut->DrawBitmapEx( rPos, aBitmapEx );
425 else
426 pOut->DrawBitmapEx( rPos, rLogicDestSize, aBitmapEx );
427 return true;
432 catch (const uno::RuntimeException& )
434 throw; // runtime errors are fatal
436 catch (const uno::Exception& e)
438 // ignore errors, no way of reporting them here
439 SAL_WARN("vcl.gdi",
440 "GDIMetaFile::ImplPlayWithRenderer: exception: " << e.Message);
443 return false;
446 void GDIMetaFile::Play( OutputDevice* pOut, const Point& rPos,
447 const Size& rSize )
449 vcl::Region aDrawClipRegion;
450 MapMode aDrawMap( GetPrefMapMode() );
451 Size aDestSize( pOut->LogicToPixel( rSize ) );
453 if( aDestSize.Width() && aDestSize.Height() )
455 GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
457 if( ImplPlayWithRenderer( pOut, rPos, rSize ) )
458 return;
460 Size aTmpPrefSize( pOut->LogicToPixel( GetPrefSize(), aDrawMap ) );
462 if( !aTmpPrefSize.Width() )
463 aTmpPrefSize.Width() = aDestSize.Width();
465 if( !aTmpPrefSize.Height() )
466 aTmpPrefSize.Height() = aDestSize.Height();
468 Fraction aScaleX( aDestSize.Width(), aTmpPrefSize.Width() );
469 Fraction aScaleY( aDestSize.Height(), aTmpPrefSize.Height() );
471 aScaleX *= aDrawMap.GetScaleX(); aDrawMap.SetScaleX( aScaleX );
472 aScaleY *= aDrawMap.GetScaleY(); aDrawMap.SetScaleY( aScaleY );
474 // #i47260# Convert logical output position to offset within
475 // the metafile's mapmode. Therefore, disable pixel offset on
476 // outdev, it's inverse mnOutOffLogicX/Y is calculated for a
477 // different mapmode (the one currently set on pOut, that is)
478 // - thus, aDrawMap's origin would generally be wrong. And
479 // even _if_ aDrawMap is similar to pOutDev's current mapmode,
480 // it's _still_ undesirable to have pixel offset unequal zero,
481 // because one would still get round-off errors (the
482 // round-trip error for LogicToPixel( PixelToLogic() ) was the
483 // reason for having pixel offset in the first place).
484 const Size& rOldOffset( pOut->GetPixelOffset() );
485 const Size aEmptySize;
486 pOut->SetPixelOffset( aEmptySize );
487 aDrawMap.SetOrigin( pOut->PixelToLogic( pOut->LogicToPixel( rPos ), aDrawMap ) );
488 pOut->SetPixelOffset( rOldOffset );
490 pOut->Push();
492 if ( pMtf && pMtf->IsRecord() && ( pOut->GetOutDevType() != OUTDEV_PRINTER ) )
493 pOut->SetRelativeMapMode( aDrawMap );
494 else
495 pOut->SetMapMode( aDrawMap );
497 // #i23407# Set backwards-compatible text language and layout mode
498 // This is necessary, since old metafiles don't even know of these
499 // recent add-ons. Newer metafiles must of course explicitly set
500 // those states.
501 pOut->SetLayoutMode( ComplexTextLayoutFlags::Default );
502 pOut->SetDigitLanguage( LANGUAGE_SYSTEM );
504 Play( pOut );
506 pOut->Pop();
510 void GDIMetaFile::Pause( bool _bPause )
512 if( m_bRecord )
514 if( _bPause )
516 if( !m_bPause )
517 Linker( m_pOutDev, false );
519 else
521 if( m_bPause )
522 Linker( m_pOutDev, true );
525 m_bPause = _bPause;
529 void GDIMetaFile::Stop()
531 if( m_bRecord )
533 m_bRecord = false;
535 if( !m_bPause )
536 Linker( m_pOutDev, false );
537 else
538 m_bPause = false;
542 void GDIMetaFile::WindStart()
544 if( !m_bRecord )
545 m_nCurrentActionElement = 0;
548 void GDIMetaFile::WindPrev()
550 if( !m_bRecord )
551 if ( m_nCurrentActionElement > 0 )
552 --m_nCurrentActionElement;
555 void GDIMetaFile::AddAction( MetaAction* pAction )
557 m_aList.push_back( pAction );
559 if( m_pPrev )
561 pAction->Duplicate();
562 m_pPrev->AddAction( pAction );
566 void GDIMetaFile::AddAction( MetaAction* pAction, size_t nPos )
568 if ( nPos < m_aList.size() )
570 ::std::vector< MetaAction* >::iterator it = m_aList.begin();
571 ::std::advance( it, nPos );
572 m_aList.insert( it, pAction );
574 else
576 m_aList.push_back( pAction );
579 if( m_pPrev )
581 pAction->Duplicate();
582 m_pPrev->AddAction( pAction, nPos );
586 void GDIMetaFile::push_back( MetaAction* pAction )
588 m_aList.push_back( pAction );
591 void GDIMetaFile::RemoveAction( size_t nPos )
593 if ( nPos < m_aList.size() )
595 ::std::vector< MetaAction* >::iterator it = m_aList.begin();
596 ::std::advance( it, nPos );
597 (*it)->Delete();
598 m_aList.erase( it );
602 if( m_pPrev )
603 m_pPrev->RemoveAction( nPos );
606 bool GDIMetaFile::Mirror( BmpMirrorFlags nMirrorFlags )
608 const Size aOldPrefSize( GetPrefSize() );
609 long nMoveX, nMoveY;
610 double fScaleX, fScaleY;
611 bool bRet;
613 if( nMirrorFlags & BmpMirrorFlags::Horizontal )
615 nMoveX = std::abs( aOldPrefSize.Width() ) - 1;
616 fScaleX = -1.0;
618 else
620 nMoveX = 0;
621 fScaleX = 1.0;
624 if( nMirrorFlags & BmpMirrorFlags::Vertical )
626 nMoveY = std::abs( aOldPrefSize.Height() ) - 1;
627 fScaleY = -1.0;
629 else
631 nMoveY = 0;
632 fScaleY = 1.0;
635 if( ( fScaleX != 1.0 ) || ( fScaleY != 1.0 ) )
637 Scale( fScaleX, fScaleY );
638 Move( nMoveX, nMoveY );
639 SetPrefSize( aOldPrefSize );
640 bRet = true;
642 else
643 bRet = false;
645 return bRet;
648 void GDIMetaFile::Move( long nX, long nY )
650 const Size aBaseOffset( nX, nY );
651 Size aOffset( aBaseOffset );
652 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
654 aMapVDev->EnableOutput( false );
655 aMapVDev->SetMapMode( GetPrefMapMode() );
657 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
659 const MetaActionType nType = pAct->GetType();
660 MetaAction* pModAct;
662 if( pAct->GetRefCount() > 1 )
664 m_aList[ m_nCurrentActionElement ] = pModAct = pAct->Clone();
665 pAct->Delete();
667 else
668 pModAct = pAct;
670 if( ( MetaActionType::MAPMODE == nType ) ||
671 ( MetaActionType::PUSH == nType ) ||
672 ( MetaActionType::POP == nType ) )
674 pModAct->Execute( aMapVDev.get() );
675 aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
678 pModAct->Move( aOffset.Width(), aOffset.Height() );
682 void GDIMetaFile::Move( long nX, long nY, long nDPIX, long nDPIY )
684 const Size aBaseOffset( nX, nY );
685 Size aOffset( aBaseOffset );
686 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
688 aMapVDev->EnableOutput( false );
689 aMapVDev->SetReferenceDevice( nDPIX, nDPIY );
690 aMapVDev->SetMapMode( GetPrefMapMode() );
692 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
694 const MetaActionType nType = pAct->GetType();
695 MetaAction* pModAct;
697 if( pAct->GetRefCount() > 1 )
699 m_aList[ m_nCurrentActionElement ] = pModAct = pAct->Clone();
700 pAct->Delete();
702 else
703 pModAct = pAct;
705 if( ( MetaActionType::MAPMODE == nType ) ||
706 ( MetaActionType::PUSH == nType ) ||
707 ( MetaActionType::POP == nType ) )
709 pModAct->Execute( aMapVDev.get() );
710 if( aMapVDev->GetMapMode().GetMapUnit() == MapUnit::MapPixel )
712 aOffset = aMapVDev->LogicToPixel( aBaseOffset, GetPrefMapMode() );
713 MapMode aMap( aMapVDev->GetMapMode() );
714 aOffset.Width() = static_cast<long>(aOffset.Width() * (double)aMap.GetScaleX());
715 aOffset.Height() = static_cast<long>(aOffset.Height() * (double)aMap.GetScaleY());
717 else
718 aOffset = OutputDevice::LogicToLogic( aBaseOffset, GetPrefMapMode(), aMapVDev->GetMapMode() );
721 pModAct->Move( aOffset.Width(), aOffset.Height() );
725 void GDIMetaFile::Scale( double fScaleX, double fScaleY )
727 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
729 MetaAction* pModAct;
731 if( pAct->GetRefCount() > 1 )
733 m_aList[ m_nCurrentActionElement ] = pModAct = pAct->Clone();
734 pAct->Delete();
736 else
737 pModAct = pAct;
739 pModAct->Scale( fScaleX, fScaleY );
742 m_aPrefSize.Width() = FRound( m_aPrefSize.Width() * fScaleX );
743 m_aPrefSize.Height() = FRound( m_aPrefSize.Height() * fScaleY );
746 void GDIMetaFile::Scale( const Fraction& rScaleX, const Fraction& rScaleY )
748 Scale( (double) rScaleX, (double) rScaleY );
751 void GDIMetaFile::Clip( const tools::Rectangle& i_rClipRect )
753 tools::Rectangle aCurRect( i_rClipRect );
754 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
756 aMapVDev->EnableOutput( false );
757 aMapVDev->SetMapMode( GetPrefMapMode() );
759 for( MetaAction* pAct = FirstAction(); pAct; pAct = NextAction() )
761 const MetaActionType nType = pAct->GetType();
763 if( ( MetaActionType::MAPMODE == nType ) ||
764 ( MetaActionType::PUSH == nType ) ||
765 ( MetaActionType::POP == nType ) )
767 pAct->Execute( aMapVDev.get() );
768 aCurRect = OutputDevice::LogicToLogic( i_rClipRect, GetPrefMapMode(), aMapVDev->GetMapMode() );
770 else if( nType == MetaActionType::CLIPREGION )
772 MetaClipRegionAction* pOldAct = static_cast<MetaClipRegionAction*>(pAct);
773 vcl::Region aNewReg( aCurRect );
774 if( pOldAct->IsClipping() )
775 aNewReg.Intersect( pOldAct->GetRegion() );
776 MetaClipRegionAction* pNewAct = new MetaClipRegionAction( aNewReg, true );
777 m_aList[ m_nCurrentActionElement ] = pNewAct;
778 pOldAct->Delete();
783 Point GDIMetaFile::ImplGetRotatedPoint( const Point& rPt, const Point& rRotatePt,
784 const Size& rOffset, double fSin, double fCos )
786 const long nX = rPt.X() - rRotatePt.X();
787 const long nY = rPt.Y() - rRotatePt.Y();
789 return Point( FRound( fCos * nX + fSin * nY ) + rRotatePt.X() + rOffset.Width(),
790 -FRound( fSin * nX - fCos * nY ) + rRotatePt.Y() + rOffset.Height() );
793 tools::Polygon GDIMetaFile::ImplGetRotatedPolygon( const tools::Polygon& rPoly, const Point& rRotatePt,
794 const Size& rOffset, double fSin, double fCos )
796 tools::Polygon aRet( rPoly );
798 aRet.Rotate( rRotatePt, fSin, fCos );
799 aRet.Move( rOffset.Width(), rOffset.Height() );
801 return aRet;
804 tools::PolyPolygon GDIMetaFile::ImplGetRotatedPolyPolygon( const tools::PolyPolygon& rPolyPoly, const Point& rRotatePt,
805 const Size& rOffset, double fSin, double fCos )
807 tools::PolyPolygon aRet( rPolyPoly );
809 aRet.Rotate( rRotatePt, fSin, fCos );
810 aRet.Move( rOffset.Width(), rOffset.Height() );
812 return aRet;
815 void GDIMetaFile::ImplAddGradientEx( GDIMetaFile& rMtf,
816 const OutputDevice& rMapDev,
817 const tools::PolyPolygon& rPolyPoly,
818 const Gradient& rGrad )
820 // Generate comment, GradientEx and Gradient actions (within DrawGradient)
821 ScopedVclPtrInstance< VirtualDevice > aVDev(rMapDev, DeviceFormat::DEFAULT);
822 aVDev->EnableOutput( false );
823 GDIMetaFile aGradMtf;
825 aGradMtf.Record( aVDev.get() );
826 aVDev->DrawGradient( rPolyPoly, rGrad );
827 aGradMtf.Stop();
829 size_t i, nAct( aGradMtf.GetActionSize() );
830 for( i=0; i < nAct; ++i )
832 MetaAction* pMetaAct = aGradMtf.GetAction( i );
833 pMetaAct->Duplicate();
834 rMtf.AddAction( pMetaAct );
838 void GDIMetaFile::Rotate( long nAngle10 )
840 nAngle10 %= 3600;
841 nAngle10 = ( nAngle10 < 0 ) ? ( 3599 + nAngle10 ) : nAngle10;
843 if( nAngle10 )
845 GDIMetaFile aMtf;
846 ScopedVclPtrInstance< VirtualDevice > aMapVDev;
847 const double fAngle = F_PI1800 * nAngle10;
848 const double fSin = sin( fAngle );
849 const double fCos = cos( fAngle );
850 tools::Rectangle aRect=tools::Rectangle( Point(), GetPrefSize() );
851 tools::Polygon aPoly( aRect );
853 aPoly.Rotate( Point(), fSin, fCos );
855 aMapVDev->EnableOutput( false );
856 aMapVDev->SetMapMode( GetPrefMapMode() );
858 const tools::Rectangle aNewBound( aPoly.GetBoundRect() );
860 const Point aOrigin( GetPrefMapMode().GetOrigin().X(), GetPrefMapMode().GetOrigin().Y() );
861 const Size aOffset( -aNewBound.Left(), -aNewBound.Top() );
863 Point aRotAnchor( aOrigin );
864 Size aRotOffset( aOffset );
866 for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
868 const MetaActionType nActionType = pAction->GetType();
870 switch( nActionType )
872 case( MetaActionType::PIXEL ):
874 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
875 aMtf.AddAction( new MetaPixelAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
876 pAct->GetColor() ) );
878 break;
880 case( MetaActionType::POINT ):
882 MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
883 aMtf.AddAction( new MetaPointAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
885 break;
887 case( MetaActionType::LINE ):
889 MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
890 aMtf.AddAction( new MetaLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
891 ImplGetRotatedPoint( pAct->GetEndPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
892 pAct->GetLineInfo() ) );
894 break;
896 case( MetaActionType::RECT ):
898 MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
899 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
901 break;
903 case( MetaActionType::ROUNDRECT ):
905 MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
906 const tools::Polygon aRoundRectPoly( pAct->GetRect(), pAct->GetHorzRound(), pAct->GetVertRound() );
908 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aRoundRectPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
910 break;
912 case( MetaActionType::ELLIPSE ):
914 MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
915 const tools::Polygon aEllipsePoly( pAct->GetRect().Center(), pAct->GetRect().GetWidth() >> 1, pAct->GetRect().GetHeight() >> 1 );
917 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aEllipsePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
919 break;
921 case( MetaActionType::ARC ):
923 MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
924 const tools::Polygon aArcPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Arc );
926 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aArcPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
928 break;
930 case( MetaActionType::PIE ):
932 MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
933 const tools::Polygon aPiePoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Pie );
935 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aPiePoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
937 break;
939 case( MetaActionType::CHORD ):
941 MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
942 const tools::Polygon aChordPoly( pAct->GetRect(), pAct->GetStartPoint(), pAct->GetEndPoint(), PolyStyle::Chord );
944 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( aChordPoly, aRotAnchor, aRotOffset, fSin, fCos ) ) );
946 break;
948 case( MetaActionType::POLYLINE ):
950 MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
951 aMtf.AddAction( new MetaPolyLineAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->GetLineInfo() ) );
953 break;
955 case( MetaActionType::POLYGON ):
957 MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
958 aMtf.AddAction( new MetaPolygonAction( ImplGetRotatedPolygon( pAct->GetPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
960 break;
962 case( MetaActionType::POLYPOLYGON ):
964 MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
965 aMtf.AddAction( new MetaPolyPolygonAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) );
967 break;
969 case( MetaActionType::TEXT ):
971 MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
972 aMtf.AddAction( new MetaTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
973 pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
975 break;
977 case( MetaActionType::TEXTARRAY ):
979 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
980 aMtf.AddAction( new MetaTextArrayAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
981 pAct->GetText(), pAct->GetDXArray(), pAct->GetIndex(), pAct->GetLen() ) );
983 break;
985 case( MetaActionType::STRETCHTEXT ):
987 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
988 aMtf.AddAction( new MetaStretchTextAction( ImplGetRotatedPoint( pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
989 pAct->GetWidth(), pAct->GetText(), pAct->GetIndex(), pAct->GetLen() ) );
991 break;
993 case( MetaActionType::TEXTLINE ):
995 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
996 aMtf.AddAction( new MetaTextLineAction( ImplGetRotatedPoint( pAct->GetStartPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
997 pAct->GetWidth(), pAct->GetStrikeout(), pAct->GetUnderline(), pAct->GetOverline() ) );
999 break;
1001 case( MetaActionType::BMPSCALE ):
1003 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1004 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1005 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1006 BitmapEx aBmpEx( pAct->GetBitmap() );
1008 aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) );
1009 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(),
1010 aBmpEx ) );
1012 break;
1014 case( MetaActionType::BMPSCALEPART ):
1016 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1017 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1018 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1019 BitmapEx aBmpEx( pAct->GetBitmap() );
1021 aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
1022 aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) );
1024 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
1026 break;
1028 case( MetaActionType::BMPEXSCALE ):
1030 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1031 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1032 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1033 BitmapEx aBmpEx( pAct->GetBitmapEx() );
1035 aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) );
1037 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
1039 break;
1041 case( MetaActionType::BMPEXSCALEPART ):
1043 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1044 tools::Polygon aBmpPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetDestPoint(), pAct->GetDestSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1045 tools::Rectangle aBmpRect( aBmpPoly.GetBoundRect() );
1046 BitmapEx aBmpEx( pAct->GetBitmapEx() );
1048 aBmpEx.Crop( tools::Rectangle( pAct->GetSrcPoint(), pAct->GetSrcSize() ) );
1049 aBmpEx.Rotate( nAngle10, Color( COL_TRANSPARENT ) );
1051 aMtf.AddAction( new MetaBmpExScaleAction( aBmpRect.TopLeft(), aBmpRect.GetSize(), aBmpEx ) );
1053 break;
1055 case( MetaActionType::GRADIENT ):
1057 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1059 ImplAddGradientEx( aMtf, *aMapVDev.get(),
1060 ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor, aRotOffset, fSin, fCos ),
1061 pAct->GetGradient() );
1063 break;
1065 case( MetaActionType::GRADIENTEX ):
1067 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1068 aMtf.AddAction( new MetaGradientExAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1069 pAct->GetGradient() ) );
1071 break;
1073 // Handle gradientex comment block correctly
1074 case( MetaActionType::COMMENT ):
1076 MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction);
1077 if( pCommentAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1079 int nBeginComments( 1 );
1080 pAction = NextAction();
1082 // skip everything, except gradientex action
1083 while( pAction )
1085 const MetaActionType nType = pAction->GetType();
1087 if( MetaActionType::GRADIENTEX == nType )
1089 // Add rotated gradientex
1090 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1091 ImplAddGradientEx( aMtf, *aMapVDev.get(),
1092 ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1093 pAct->GetGradient() );
1095 else if( MetaActionType::COMMENT == nType)
1097 MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pAction);
1098 if( pAct->GetComment() == "XGRAD_SEQ_END" )
1100 // handle nested blocks
1101 --nBeginComments;
1103 // gradientex comment block: end reached, done.
1104 if( !nBeginComments )
1105 break;
1107 else if( pAct->GetComment() == "XGRAD_SEQ_BEGIN" )
1109 // handle nested blocks
1110 ++nBeginComments;
1115 pAction =NextAction();
1118 else
1120 bool bPathStroke = (pCommentAct->GetComment() == "XPATHSTROKE_SEQ_BEGIN");
1121 if ( bPathStroke || pCommentAct->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1123 if ( pCommentAct->GetDataSize() )
1125 SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pCommentAct->GetData()), pCommentAct->GetDataSize(), StreamMode::READ );
1126 SvMemoryStream aDest;
1127 if ( bPathStroke )
1129 SvtGraphicStroke aStroke;
1130 ReadSvtGraphicStroke( aMemStm, aStroke );
1131 tools::Polygon aPath;
1132 aStroke.getPath( aPath );
1133 aStroke.setPath( ImplGetRotatedPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1134 WriteSvtGraphicStroke( aDest, aStroke );
1135 aMtf.AddAction( new MetaCommentAction( "XPATHSTROKE_SEQ_BEGIN", 0,
1136 static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1138 else
1140 SvtGraphicFill aFill;
1141 ReadSvtGraphicFill( aMemStm, aFill );
1142 tools::PolyPolygon aPath;
1143 aFill.getPath( aPath );
1144 aFill.setPath( ImplGetRotatedPolyPolygon( aPath, aRotAnchor, aRotOffset, fSin, fCos ) );
1145 WriteSvtGraphicFill( aDest, aFill );
1146 aMtf.AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0,
1147 static_cast<const sal_uInt8*>( aDest.GetData()), aDest.Tell() ) );
1151 else if ( pCommentAct->GetComment() == "XPATHSTROKE_SEQ_END"
1152 || pCommentAct->GetComment() == "XPATHFILL_SEQ_END" )
1154 pAction->Execute( aMapVDev.get() );
1155 pAction->Duplicate();
1156 aMtf.AddAction( pAction );
1160 break;
1162 case( MetaActionType::HATCH ):
1164 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
1165 Hatch aHatch( pAct->GetHatch() );
1167 aHatch.SetAngle( aHatch.GetAngle() + (sal_uInt16) nAngle10 );
1168 aMtf.AddAction( new MetaHatchAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1169 aHatch ) );
1171 break;
1173 case( MetaActionType::Transparent ):
1175 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1176 aMtf.AddAction( new MetaTransparentAction( ImplGetRotatedPolyPolygon( pAct->GetPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ),
1177 pAct->GetTransparence() ) );
1179 break;
1181 case( MetaActionType::FLOATTRANSPARENT ):
1183 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1184 GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
1185 tools::Polygon aMtfPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1186 tools::Rectangle aMtfRect( aMtfPoly.GetBoundRect() );
1188 aTransMtf.Rotate( nAngle10 );
1189 aMtf.AddAction( new MetaFloatTransparentAction( aTransMtf, aMtfRect.TopLeft(), aMtfRect.GetSize(),
1190 pAct->GetGradient() ) );
1192 break;
1194 case( MetaActionType::EPS ):
1196 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
1197 GDIMetaFile aEPSMtf( pAct->GetSubstitute() );
1198 tools::Polygon aEPSPoly( ImplGetRotatedPolygon( tools::Rectangle( pAct->GetPoint(), pAct->GetSize() ), aRotAnchor, aRotOffset, fSin, fCos ) );
1199 tools::Rectangle aEPSRect( aEPSPoly.GetBoundRect() );
1201 aEPSMtf.Rotate( nAngle10 );
1202 aMtf.AddAction( new MetaEPSAction( aEPSRect.TopLeft(), aEPSRect.GetSize(),
1203 pAct->GetLink(), aEPSMtf ) );
1205 break;
1207 case( MetaActionType::CLIPREGION ):
1209 MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1211 if( pAct->IsClipping() && pAct->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1212 aMtf.AddAction( new MetaClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( pAct->GetRegion().GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ), true ) );
1213 else
1215 pAction->Duplicate();
1216 aMtf.AddAction( pAction );
1219 break;
1221 case( MetaActionType::ISECTRECTCLIPREGION ):
1223 MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1224 aMtf.AddAction( new MetaISectRegionClipRegionAction(vcl::Region(
1225 ImplGetRotatedPolygon( pAct->GetRect(), aRotAnchor,
1226 aRotOffset, fSin, fCos )) ) );
1228 break;
1230 case( MetaActionType::ISECTREGIONCLIPREGION ):
1232 MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1233 const vcl::Region& rRegion = pAct->GetRegion();
1235 if( rRegion.HasPolyPolygonOrB2DPolyPolygon() )
1236 aMtf.AddAction( new MetaISectRegionClipRegionAction( vcl::Region( ImplGetRotatedPolyPolygon( rRegion.GetAsPolyPolygon(), aRotAnchor, aRotOffset, fSin, fCos ) ) ) );
1237 else
1239 pAction->Duplicate();
1240 aMtf.AddAction( pAction );
1243 break;
1245 case( MetaActionType::REFPOINT ):
1247 MetaRefPointAction* pAct = static_cast<MetaRefPointAction*>(pAction);
1248 aMtf.AddAction( new MetaRefPointAction( ImplGetRotatedPoint( pAct->GetRefPoint(), aRotAnchor, aRotOffset, fSin, fCos ), pAct->IsSetting() ) );
1250 break;
1252 case( MetaActionType::FONT ):
1254 MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1255 vcl::Font aFont( pAct->GetFont() );
1257 aFont.SetOrientation( aFont.GetOrientation() + (sal_uInt16) nAngle10 );
1258 aMtf.AddAction( new MetaFontAction( aFont ) );
1260 break;
1262 case( MetaActionType::BMP ):
1263 case( MetaActionType::BMPEX ):
1264 case( MetaActionType::MASK ):
1265 case( MetaActionType::MASKSCALE ):
1266 case( MetaActionType::MASKSCALEPART ):
1267 case( MetaActionType::WALLPAPER ):
1268 case( MetaActionType::TEXTRECT ):
1269 case( MetaActionType::MOVECLIPREGION ):
1271 OSL_FAIL( "GDIMetaFile::Rotate(): unsupported action" );
1273 break;
1275 default:
1277 pAction->Execute( aMapVDev.get() );
1278 pAction->Duplicate();
1279 aMtf.AddAction( pAction );
1281 // update rotation point and offset, if necessary
1282 if( ( MetaActionType::MAPMODE == nActionType ) ||
1283 ( MetaActionType::PUSH == nActionType ) ||
1284 ( MetaActionType::POP == nActionType ) )
1286 aRotAnchor = OutputDevice::LogicToLogic( aOrigin, m_aPrefMapMode, aMapVDev->GetMapMode() );
1287 aRotOffset = OutputDevice::LogicToLogic( aOffset, m_aPrefMapMode, aMapVDev->GetMapMode() );
1290 break;
1294 aMtf.m_aPrefMapMode = m_aPrefMapMode;
1295 aMtf.m_aPrefSize = aNewBound.GetSize();
1297 *this = aMtf;
1301 static void ImplActionBounds( tools::Rectangle& o_rOutBounds,
1302 const tools::Rectangle& i_rInBounds,
1303 const std::vector<tools::Rectangle>& i_rClipStack,
1304 tools::Rectangle* o_pHairline )
1306 tools::Rectangle aBounds( i_rInBounds );
1307 if( ! i_rInBounds.IsEmpty() && ! i_rClipStack.empty() && ! i_rClipStack.back().IsEmpty() )
1308 aBounds.Intersection( i_rClipStack.back() );
1309 if( ! aBounds.IsEmpty() )
1311 if( ! o_rOutBounds.IsEmpty() )
1312 o_rOutBounds.Union( aBounds );
1313 else
1314 o_rOutBounds = aBounds;
1316 if(o_pHairline)
1318 if( ! o_pHairline->IsEmpty() )
1319 o_pHairline->Union( aBounds );
1320 else
1321 *o_pHairline = aBounds;
1326 tools::Rectangle GDIMetaFile::GetBoundRect( OutputDevice& i_rReference, tools::Rectangle* pHairline ) const
1328 GDIMetaFile aMtf;
1329 ScopedVclPtrInstance< VirtualDevice > aMapVDev( i_rReference );
1331 aMapVDev->EnableOutput( false );
1332 aMapVDev->SetMapMode( GetPrefMapMode() );
1334 std::vector<tools::Rectangle> aClipStack( 1, tools::Rectangle() );
1335 std::vector<PushFlags> aPushFlagStack;
1337 tools::Rectangle aBound;
1339 if(pHairline)
1340 *pHairline = tools::Rectangle();
1342 const sal_uLong nCount(GetActionSize());
1344 for(sal_uLong a(0); a < nCount; a++)
1346 MetaAction* pAction = GetAction(a);
1347 const MetaActionType nActionType = pAction->GetType();
1348 tools::Rectangle* pUseHairline = (pHairline && aMapVDev->IsLineColor()) ? pHairline : nullptr;
1350 switch( nActionType )
1352 case( MetaActionType::PIXEL ):
1354 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1355 ImplActionBounds( aBound,
1356 tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1357 aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1358 aClipStack, pUseHairline );
1360 break;
1362 case( MetaActionType::POINT ):
1364 MetaPointAction* pAct = static_cast<MetaPointAction*>(pAction);
1365 ImplActionBounds( aBound,
1366 tools::Rectangle( OutputDevice::LogicToLogic( pAct->GetPoint(), aMapVDev->GetMapMode(), GetPrefMapMode() ),
1367 aMapVDev->PixelToLogic( Size( 1, 1 ), GetPrefMapMode() ) ),
1368 aClipStack, pUseHairline );
1370 break;
1372 case( MetaActionType::LINE ):
1374 MetaLineAction* pAct = static_cast<MetaLineAction*>(pAction);
1375 Point aP1( pAct->GetStartPoint() ), aP2( pAct->GetEndPoint() );
1376 tools::Rectangle aRect( aP1, aP2 );
1377 aRect.Justify();
1379 if(pUseHairline)
1381 const LineInfo& rLineInfo = pAct->GetLineInfo();
1383 if(0 != rLineInfo.GetWidth())
1384 pUseHairline = nullptr;
1387 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1389 break;
1391 case( MetaActionType::RECT ):
1393 MetaRectAction* pAct = static_cast<MetaRectAction*>(pAction);
1394 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1396 break;
1398 case( MetaActionType::ROUNDRECT ):
1400 MetaRoundRectAction* pAct = static_cast<MetaRoundRectAction*>(pAction);
1401 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1403 break;
1405 case( MetaActionType::ELLIPSE ):
1407 MetaEllipseAction* pAct = static_cast<MetaEllipseAction*>(pAction);
1408 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1410 break;
1412 case( MetaActionType::ARC ):
1414 MetaArcAction* pAct = static_cast<MetaArcAction*>(pAction);
1415 // FIXME: this is imprecise
1416 // e.g. for small arcs the whole rectangle is WAY too large
1417 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1419 break;
1421 case( MetaActionType::PIE ):
1423 MetaPieAction* pAct = static_cast<MetaPieAction*>(pAction);
1424 // FIXME: this is imprecise
1425 // e.g. for small arcs the whole rectangle is WAY too large
1426 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1428 break;
1430 case( MetaActionType::CHORD ):
1432 MetaChordAction* pAct = static_cast<MetaChordAction*>(pAction);
1433 // FIXME: this is imprecise
1434 // e.g. for small arcs the whole rectangle is WAY too large
1435 ImplActionBounds( aBound, OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1437 break;
1439 case( MetaActionType::POLYLINE ):
1441 MetaPolyLineAction* pAct = static_cast<MetaPolyLineAction*>(pAction);
1442 tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1444 if(pUseHairline)
1446 const LineInfo& rLineInfo = pAct->GetLineInfo();
1448 if(0 != rLineInfo.GetWidth())
1449 pUseHairline = nullptr;
1452 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1454 break;
1456 case( MetaActionType::POLYGON ):
1458 MetaPolygonAction* pAct = static_cast<MetaPolygonAction*>(pAction);
1459 tools::Rectangle aRect( pAct->GetPolygon().GetBoundRect() );
1460 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1462 break;
1464 case( MetaActionType::POLYPOLYGON ):
1466 MetaPolyPolygonAction* pAct = static_cast<MetaPolyPolygonAction*>(pAction);
1467 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1468 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, pUseHairline );
1470 break;
1472 case( MetaActionType::TEXT ):
1474 MetaTextAction* pAct = static_cast<MetaTextAction*>(pAction);
1475 tools::Rectangle aRect;
1476 // hdu said base = index
1477 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen() );
1478 Point aPt( pAct->GetPoint() );
1479 aRect.Move( aPt.X(), aPt.Y() );
1480 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1482 break;
1484 case( MetaActionType::TEXTARRAY ):
1486 MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pAction);
1487 tools::Rectangle aRect;
1488 // hdu said base = index
1489 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1490 0, pAct->GetDXArray() );
1491 Point aPt( pAct->GetPoint() );
1492 aRect.Move( aPt.X(), aPt.Y() );
1493 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1495 break;
1497 case( MetaActionType::STRETCHTEXT ):
1499 MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pAction);
1500 tools::Rectangle aRect;
1501 // hdu said base = index
1502 aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
1503 pAct->GetWidth() );
1504 Point aPt( pAct->GetPoint() );
1505 aRect.Move( aPt.X(), aPt.Y() );
1506 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1508 break;
1510 case( MetaActionType::TEXTLINE ):
1512 MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pAction);
1513 // measure a test string to get ascend and descent right
1514 static const sal_Unicode pStr[] = { 0xc4, 0x67, 0 };
1515 OUString aStr( pStr );
1517 tools::Rectangle aRect;
1518 aMapVDev->GetTextBoundRect( aRect, aStr, 0, 0, aStr.getLength() );
1519 Point aPt( pAct->GetStartPoint() );
1520 aRect.Move( aPt.X(), aPt.Y() );
1521 aRect.Right() = aRect.Left() + pAct->GetWidth();
1522 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1524 break;
1526 case( MetaActionType::BMPSCALE ):
1528 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1529 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1530 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1532 break;
1534 case( MetaActionType::BMPSCALEPART ):
1536 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1537 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1538 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1540 break;
1542 case( MetaActionType::BMPEXSCALE ):
1544 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
1545 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1546 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1548 break;
1550 case( MetaActionType::BMPEXSCALEPART ):
1552 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
1553 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1554 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1556 break;
1558 case( MetaActionType::GRADIENT ):
1560 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
1561 tools::Rectangle aRect( pAct->GetRect() );
1562 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1564 break;
1566 case( MetaActionType::GRADIENTEX ):
1568 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
1569 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1570 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1572 break;
1574 case( MetaActionType::COMMENT ):
1576 // nothing to do
1578 break;
1580 case( MetaActionType::HATCH ):
1582 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
1583 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1584 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1586 break;
1588 case( MetaActionType::Transparent ):
1590 MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pAction);
1591 tools::Rectangle aRect( pAct->GetPolyPolygon().GetBoundRect() );
1592 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1594 break;
1596 case( MetaActionType::FLOATTRANSPARENT ):
1598 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
1599 // MetaFloatTransparentAction is defined limiting its content Metafile
1600 // to its geometry definition(Point, Size), so use these directly
1601 const tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1602 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1604 break;
1606 case( MetaActionType::EPS ):
1608 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
1609 tools::Rectangle aRect( pAct->GetPoint(), pAct->GetSize() );
1610 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1612 break;
1614 case( MetaActionType::CLIPREGION ):
1616 MetaClipRegionAction* pAct = static_cast<MetaClipRegionAction*>(pAction);
1617 if( pAct->IsClipping() )
1618 aClipStack.back() = OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() );
1619 else
1620 aClipStack.back() = tools::Rectangle();
1622 break;
1624 case( MetaActionType::ISECTRECTCLIPREGION ):
1626 MetaISectRectClipRegionAction* pAct = static_cast<MetaISectRectClipRegionAction*>(pAction);
1627 tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1628 if( aClipStack.back().IsEmpty() )
1629 aClipStack.back() = aRect;
1630 else
1631 aClipStack.back().Intersection( aRect );
1633 break;
1635 case( MetaActionType::ISECTREGIONCLIPREGION ):
1637 MetaISectRegionClipRegionAction* pAct = static_cast<MetaISectRegionClipRegionAction*>(pAction);
1638 tools::Rectangle aRect( OutputDevice::LogicToLogic( pAct->GetRegion().GetBoundRect(), aMapVDev->GetMapMode(), GetPrefMapMode() ) );
1639 if( aClipStack.back().IsEmpty() )
1640 aClipStack.back() = aRect;
1641 else
1642 aClipStack.back().Intersection( aRect );
1644 break;
1646 case( MetaActionType::BMP ):
1648 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
1649 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1650 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1652 break;
1654 case( MetaActionType::BMPEX ):
1656 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
1657 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmapEx().GetSizePixel() ) );
1658 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1660 break;
1662 case( MetaActionType::MASK ):
1664 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
1665 tools::Rectangle aRect( pAct->GetPoint(), aMapVDev->PixelToLogic( pAct->GetBitmap().GetSizePixel() ) );
1666 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1668 break;
1670 case( MetaActionType::MASKSCALE ):
1672 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1673 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1674 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1676 break;
1678 case( MetaActionType::MASKSCALEPART ):
1680 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
1681 tools::Rectangle aRect( pAct->GetDestPoint(), pAct->GetDestSize() );
1682 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1684 break;
1686 case( MetaActionType::WALLPAPER ):
1688 MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
1689 tools::Rectangle aRect( pAct->GetRect() );
1690 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1692 break;
1694 case( MetaActionType::TEXTRECT ):
1696 MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pAction);
1697 tools::Rectangle aRect( pAct->GetRect() );
1698 ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack, nullptr );
1700 break;
1702 case( MetaActionType::MOVECLIPREGION ):
1704 MetaMoveClipRegionAction* pAct = static_cast<MetaMoveClipRegionAction*>(pAction);
1705 if( ! aClipStack.back().IsEmpty() )
1707 Size aDelta( pAct->GetHorzMove(), pAct->GetVertMove() );
1708 aDelta = OutputDevice::LogicToLogic( aDelta, aMapVDev->GetMapMode(), GetPrefMapMode() );
1709 aClipStack.back().Move( aDelta.Width(), aDelta.Width() );
1712 break;
1714 default:
1716 pAction->Execute( aMapVDev.get() );
1718 if( nActionType == MetaActionType::PUSH )
1720 MetaPushAction* pAct = static_cast<MetaPushAction*>(pAction);
1721 aPushFlagStack.push_back( pAct->GetFlags() );
1722 if( aPushFlagStack.back() & PushFlags::CLIPREGION )
1724 tools::Rectangle aRect( aClipStack.back() );
1725 aClipStack.push_back( aRect );
1728 else if( nActionType == MetaActionType::POP )
1730 // sanity check
1731 if( ! aPushFlagStack.empty() )
1733 if( aPushFlagStack.back() & PushFlags::CLIPREGION )
1735 if( aClipStack.size() > 1 )
1736 aClipStack.pop_back();
1738 aPushFlagStack.pop_back();
1742 break;
1745 return aBound;
1748 Color GDIMetaFile::ImplColAdjustFnc( const Color& rColor, const void* pColParam )
1750 return Color( rColor.GetTransparency(),
1751 static_cast<const ImplColAdjustParam*>(pColParam)->pMapR[ rColor.GetRed() ],
1752 static_cast<const ImplColAdjustParam*>(pColParam)->pMapG[ rColor.GetGreen() ],
1753 static_cast<const ImplColAdjustParam*>(pColParam)->pMapB[ rColor.GetBlue() ] );
1757 BitmapEx GDIMetaFile::ImplBmpAdjustFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1759 const ImplBmpAdjustParam* p = static_cast<const ImplBmpAdjustParam*>(pBmpParam);
1760 BitmapEx aRet( rBmpEx );
1762 aRet.Adjust( p->nLuminancePercent, p->nContrastPercent,
1763 p->nChannelRPercent, p->nChannelGPercent, p->nChannelBPercent,
1764 p->fGamma, p->bInvert );
1766 return aRet;
1769 Color GDIMetaFile::ImplColConvertFnc( const Color& rColor, const void* pColParam )
1771 sal_uInt8 cLum = rColor.GetLuminance();
1773 if( MtfConversion::N1BitThreshold == static_cast<const ImplColConvertParam*>(pColParam)->eConversion )
1774 cLum = ( cLum < 128 ) ? 0 : 255;
1776 return Color( rColor.GetTransparency(), cLum, cLum, cLum );
1779 BitmapEx GDIMetaFile::ImplBmpConvertFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1781 BitmapEx aRet( rBmpEx );
1783 aRet.Convert( static_cast<const ImplBmpConvertParam*>(pBmpParam)->eConversion );
1785 return aRet;
1788 Color GDIMetaFile::ImplColMonoFnc( const Color&, const void* pColParam )
1790 return static_cast<const ImplColMonoParam*>(pColParam)->aColor;
1793 BitmapEx GDIMetaFile::ImplBmpMonoFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1795 BitmapPalette aPal( 3 );
1797 aPal[ 0 ] = Color( COL_BLACK );
1798 aPal[ 1 ] = Color( COL_WHITE );
1799 aPal[ 2 ] = static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor;
1801 Bitmap aBmp( rBmpEx.GetSizePixel(), 4, &aPal );
1802 aBmp.Erase( static_cast<const ImplBmpMonoParam*>(pBmpParam)->aColor );
1804 if( rBmpEx.IsAlpha() )
1805 return BitmapEx( aBmp, rBmpEx.GetAlpha() );
1806 else if( rBmpEx.IsTransparent() )
1807 return BitmapEx( aBmp, rBmpEx.GetMask() );
1808 else
1809 return aBmp;
1812 Color GDIMetaFile::ImplColReplaceFnc( const Color& rColor, const void* pColParam )
1814 const sal_uLong nR = rColor.GetRed(), nG = rColor.GetGreen(), nB = rColor.GetBlue();
1816 for( sal_uLong i = 0; i < static_cast<const ImplColReplaceParam*>(pColParam)->nCount; i++ )
1818 if( ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinR[ i ] <= nR ) &&
1819 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxR[ i ] >= nR ) &&
1820 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinG[ i ] <= nG ) &&
1821 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxG[ i ] >= nG ) &&
1822 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMinB[ i ] <= nB ) &&
1823 ( static_cast<const ImplColReplaceParam*>(pColParam)->pMaxB[ i ] >= nB ) )
1825 return static_cast<const ImplColReplaceParam*>(pColParam)->pDstCols[ i ];
1829 return rColor;
1832 BitmapEx GDIMetaFile::ImplBmpReplaceFnc( const BitmapEx& rBmpEx, const void* pBmpParam )
1834 const ImplBmpReplaceParam* p = static_cast<const ImplBmpReplaceParam*>(pBmpParam);
1835 BitmapEx aRet( rBmpEx );
1837 aRet.Replace( p->pSrcCols, p->pDstCols, p->nCount, p->pTols );
1839 return aRet;
1842 void GDIMetaFile::ImplExchangeColors( ColorExchangeFnc pFncCol, const void* pColParam,
1843 BmpExchangeFnc pFncBmp, const void* pBmpParam )
1845 GDIMetaFile aMtf;
1847 aMtf.m_aPrefSize = m_aPrefSize;
1848 aMtf.m_aPrefMapMode = m_aPrefMapMode;
1849 aMtf.m_bUseCanvas = m_bUseCanvas;
1851 for( MetaAction* pAction = FirstAction(); pAction; pAction = NextAction() )
1853 const MetaActionType nType = pAction->GetType();
1855 switch( nType )
1857 case( MetaActionType::PIXEL ):
1859 MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
1860 aMtf.push_back( new MetaPixelAction( pAct->GetPoint(), pFncCol( pAct->GetColor(), pColParam ) ) );
1862 break;
1864 case( MetaActionType::LINECOLOR ):
1866 MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction);
1868 if( !pAct->IsSetting() )
1869 pAct->Duplicate();
1870 else
1871 pAct = new MetaLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1873 aMtf.push_back( pAct );
1875 break;
1877 case( MetaActionType::FILLCOLOR ):
1879 MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction);
1881 if( !pAct->IsSetting() )
1882 pAct->Duplicate();
1883 else
1884 pAct = new MetaFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1886 aMtf.push_back( pAct );
1888 break;
1890 case( MetaActionType::TEXTCOLOR ):
1892 MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction);
1893 aMtf.push_back( new MetaTextColorAction( pFncCol( pAct->GetColor(), pColParam ) ) );
1895 break;
1897 case( MetaActionType::TEXTFILLCOLOR ):
1899 MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction);
1901 if( !pAct->IsSetting() )
1902 pAct->Duplicate();
1903 else
1904 pAct = new MetaTextFillColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1906 aMtf.push_back( pAct );
1908 break;
1910 case( MetaActionType::TEXTLINECOLOR ):
1912 MetaTextLineColorAction* pAct = static_cast<MetaTextLineColorAction*>(pAction);
1914 if( !pAct->IsSetting() )
1915 pAct->Duplicate();
1916 else
1917 pAct = new MetaTextLineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1919 aMtf.push_back( pAct );
1921 break;
1923 case( MetaActionType::OVERLINECOLOR ):
1925 MetaOverlineColorAction* pAct = static_cast<MetaOverlineColorAction*>(pAction);
1927 if( !pAct->IsSetting() )
1928 pAct->Duplicate();
1929 else
1930 pAct = new MetaOverlineColorAction( pFncCol( pAct->GetColor(), pColParam ), true );
1932 aMtf.push_back( pAct );
1934 break;
1936 case( MetaActionType::FONT ):
1938 MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
1939 vcl::Font aFont( pAct->GetFont() );
1941 aFont.SetColor( pFncCol( aFont.GetColor(), pColParam ) );
1942 aFont.SetFillColor( pFncCol( aFont.GetFillColor(), pColParam ) );
1943 aMtf.push_back( new MetaFontAction( aFont ) );
1945 break;
1947 case( MetaActionType::WALLPAPER ):
1949 MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
1950 Wallpaper aWall( pAct->GetWallpaper() );
1951 const tools::Rectangle& rRect = pAct->GetRect();
1953 aWall.SetColor( pFncCol( aWall.GetColor(), pColParam ) );
1955 if( aWall.IsBitmap() )
1956 aWall.SetBitmap( pFncBmp( aWall.GetBitmap(), pBmpParam ) );
1958 if( aWall.IsGradient() )
1960 Gradient aGradient( aWall.GetGradient() );
1962 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
1963 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
1964 aWall.SetGradient( aGradient );
1967 aMtf.push_back( new MetaWallpaperAction( rRect, aWall ) );
1969 break;
1971 case( MetaActionType::BMP ):
1972 case( MetaActionType::BMPEX ):
1973 case( MetaActionType::MASK ):
1975 OSL_FAIL( "Don't use bitmap actions of this type in metafiles!" );
1977 break;
1979 case( MetaActionType::BMPSCALE ):
1981 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
1982 aMtf.push_back( new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(),
1983 pFncBmp( pAct->GetBitmap(), pBmpParam ).GetBitmap() ) );
1985 break;
1987 case( MetaActionType::BMPSCALEPART ):
1989 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
1990 aMtf.push_back( new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
1991 pAct->GetSrcPoint(), pAct->GetSrcSize(),
1992 pFncBmp( pAct->GetBitmap(), pBmpParam ).GetBitmap() )
1995 break;
1997 case( MetaActionType::BMPEXSCALE ):
1999 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
2000 aMtf.push_back( new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(),
2001 pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
2004 break;
2006 case( MetaActionType::BMPEXSCALEPART ):
2008 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
2009 aMtf.push_back( new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
2010 pAct->GetSrcPoint(), pAct->GetSrcSize(),
2011 pFncBmp( pAct->GetBitmapEx(), pBmpParam ) )
2014 break;
2016 case( MetaActionType::MASKSCALE ):
2018 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
2019 aMtf.push_back( new MetaMaskScaleAction( pAct->GetPoint(), pAct->GetSize(),
2020 pAct->GetBitmap(),
2021 pFncCol( pAct->GetColor(), pColParam ) )
2024 break;
2026 case( MetaActionType::MASKSCALEPART ):
2028 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
2029 aMtf.push_back( new MetaMaskScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
2030 pAct->GetSrcPoint(), pAct->GetSrcSize(),
2031 pAct->GetBitmap(),
2032 pFncCol( pAct->GetColor(), pColParam ) )
2035 break;
2037 case( MetaActionType::GRADIENT ):
2039 MetaGradientAction* pAct = static_cast<MetaGradientAction*>(pAction);
2040 Gradient aGradient( pAct->GetGradient() );
2042 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
2043 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
2044 aMtf.push_back( new MetaGradientAction( pAct->GetRect(), aGradient ) );
2046 break;
2048 case( MetaActionType::GRADIENTEX ):
2050 MetaGradientExAction* pAct = static_cast<MetaGradientExAction*>(pAction);
2051 Gradient aGradient( pAct->GetGradient() );
2053 aGradient.SetStartColor( pFncCol( aGradient.GetStartColor(), pColParam ) );
2054 aGradient.SetEndColor( pFncCol( aGradient.GetEndColor(), pColParam ) );
2055 aMtf.push_back( new MetaGradientExAction( pAct->GetPolyPolygon(), aGradient ) );
2057 break;
2059 case( MetaActionType::HATCH ):
2061 MetaHatchAction* pAct = static_cast<MetaHatchAction*>(pAction);
2062 Hatch aHatch( pAct->GetHatch() );
2064 aHatch.SetColor( pFncCol( aHatch.GetColor(), pColParam ) );
2065 aMtf.push_back( new MetaHatchAction( pAct->GetPolyPolygon(), aHatch ) );
2067 break;
2069 case( MetaActionType::FLOATTRANSPARENT ):
2071 MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pAction);
2072 GDIMetaFile aTransMtf( pAct->GetGDIMetaFile() );
2074 aTransMtf.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2075 aMtf.push_back( new MetaFloatTransparentAction( aTransMtf,
2076 pAct->GetPoint(), pAct->GetSize(),
2077 pAct->GetGradient() )
2080 break;
2082 case( MetaActionType::EPS ):
2084 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
2085 GDIMetaFile aSubst( pAct->GetSubstitute() );
2087 aSubst.ImplExchangeColors( pFncCol, pColParam, pFncBmp, pBmpParam );
2088 aMtf.push_back( new MetaEPSAction( pAct->GetPoint(), pAct->GetSize(),
2089 pAct->GetLink(), aSubst )
2092 break;
2094 default:
2096 pAction->Duplicate();
2097 aMtf.push_back( pAction );
2099 break;
2103 *this = aMtf;
2106 void GDIMetaFile::Adjust( short nLuminancePercent, short nContrastPercent,
2107 short nChannelRPercent, short nChannelGPercent,
2108 short nChannelBPercent, double fGamma, bool bInvert, bool msoBrightness )
2110 // nothing to do? => return quickly
2111 if( nLuminancePercent || nContrastPercent ||
2112 nChannelRPercent || nChannelGPercent || nChannelBPercent ||
2113 ( fGamma != 1.0 ) || bInvert )
2115 double fM, fROff, fGOff, fBOff, fOff;
2116 ImplColAdjustParam aColParam;
2117 ImplBmpAdjustParam aBmpParam;
2119 aColParam.pMapR = new sal_uInt8[ 256 ];
2120 aColParam.pMapG = new sal_uInt8[ 256 ];
2121 aColParam.pMapB = new sal_uInt8[ 256 ];
2123 // calculate slope
2124 if( nContrastPercent >= 0 )
2125 fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0, 100 ) );
2126 else
2127 fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100, 0 ) ) / 128.0;
2129 if(!msoBrightness)
2130 // total offset = luminance offset + contrast offset
2131 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55 + 128.0 - fM * 128.0;
2132 else
2133 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55;
2135 // channel offset = channel offset + total offset
2136 fROff = nChannelRPercent * 2.55 + fOff;
2137 fGOff = nChannelGPercent * 2.55 + fOff;
2138 fBOff = nChannelBPercent * 2.55 + fOff;
2140 // calculate gamma value
2141 fGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
2142 const bool bGamma = ( fGamma != 1.0 );
2144 // create mapping table
2145 for( long nX = 0; nX < 256; nX++ )
2147 if(!msoBrightness)
2149 aColParam.pMapR[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fROff ), 0, 255 );
2150 aColParam.pMapG[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fGOff ), 0, 255 );
2151 aColParam.pMapB[ nX ] = (sal_uInt8) MinMax( FRound( nX * fM + fBOff ), 0, 255 );
2153 else
2155 aColParam.pMapR[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fROff/2-128) * fM + 128 + fROff/2 ), 0, 255 );
2156 aColParam.pMapG[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fGOff/2-128) * fM + 128 + fGOff/2 ), 0, 255 );
2157 aColParam.pMapB[ nX ] = (sal_uInt8) MinMax( FRound( (nX+fBOff/2-128) * fM + 128 + fBOff/2 ), 0, 255 );
2159 if( bGamma )
2161 aColParam.pMapR[ nX ] = GAMMA( aColParam.pMapR[ nX ], fGamma );
2162 aColParam.pMapG[ nX ] = GAMMA( aColParam.pMapG[ nX ], fGamma );
2163 aColParam.pMapB[ nX ] = GAMMA( aColParam.pMapB[ nX ], fGamma );
2166 if( bInvert )
2168 aColParam.pMapR[ nX ] = ~aColParam.pMapR[ nX ];
2169 aColParam.pMapG[ nX ] = ~aColParam.pMapG[ nX ];
2170 aColParam.pMapB[ nX ] = ~aColParam.pMapB[ nX ];
2174 aBmpParam.nLuminancePercent = nLuminancePercent;
2175 aBmpParam.nContrastPercent = nContrastPercent;
2176 aBmpParam.nChannelRPercent = nChannelRPercent;
2177 aBmpParam.nChannelGPercent = nChannelGPercent;
2178 aBmpParam.nChannelBPercent = nChannelBPercent;
2179 aBmpParam.fGamma = fGamma;
2180 aBmpParam.bInvert = bInvert;
2182 // do color adjustment
2183 ImplExchangeColors( ImplColAdjustFnc, &aColParam, ImplBmpAdjustFnc, &aBmpParam );
2185 delete[] aColParam.pMapR;
2186 delete[] aColParam.pMapG;
2187 delete[] aColParam.pMapB;
2191 void GDIMetaFile::Convert( MtfConversion eConversion )
2193 ImplColConvertParam aColParam;
2194 ImplBmpConvertParam aBmpParam;
2196 aColParam.eConversion = eConversion;
2197 aBmpParam.eConversion = ( MtfConversion::N1BitThreshold == eConversion ) ? BmpConversion::N1BitThreshold : BmpConversion::N8BitGreys;
2199 ImplExchangeColors( ImplColConvertFnc, &aColParam, ImplBmpConvertFnc, &aBmpParam );
2202 void GDIMetaFile::ReplaceColors( const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount )
2204 ImplColReplaceParam aColParam;
2205 ImplBmpReplaceParam aBmpParam;
2207 aColParam.pMinR = new sal_uLong[ nColorCount ];
2208 aColParam.pMaxR = new sal_uLong[ nColorCount ];
2209 aColParam.pMinG = new sal_uLong[ nColorCount ];
2210 aColParam.pMaxG = new sal_uLong[ nColorCount ];
2211 aColParam.pMinB = new sal_uLong[ nColorCount ];
2212 aColParam.pMaxB = new sal_uLong[ nColorCount ];
2214 for( sal_uLong i = 0; i < nColorCount; i++ )
2216 long nVal;
2218 nVal = pSearchColors[ i ].GetRed();
2219 aColParam.pMinR[ i ] = (sal_uLong) std::max( nVal, 0L );
2220 aColParam.pMaxR[ i ] = (sal_uLong) std::min( nVal, 255L );
2222 nVal = pSearchColors[ i ].GetGreen();
2223 aColParam.pMinG[ i ] = (sal_uLong) std::max( nVal, 0L );
2224 aColParam.pMaxG[ i ] = (sal_uLong) std::min( nVal, 255L );
2226 nVal = pSearchColors[ i ].GetBlue();
2227 aColParam.pMinB[ i ] = (sal_uLong) std::max( nVal, 0L );
2228 aColParam.pMaxB[ i ] = (sal_uLong) std::min( nVal, 255L );
2231 aColParam.pDstCols = pReplaceColors;
2232 aColParam.nCount = nColorCount;
2234 aBmpParam.pSrcCols = pSearchColors;
2235 aBmpParam.pDstCols = pReplaceColors;
2236 aBmpParam.nCount = nColorCount;
2237 aBmpParam.pTols = nullptr;
2239 ImplExchangeColors( ImplColReplaceFnc, &aColParam, ImplBmpReplaceFnc, &aBmpParam );
2241 delete[] aColParam.pMinR;
2242 delete[] aColParam.pMaxR;
2243 delete[] aColParam.pMinG;
2244 delete[] aColParam.pMaxG;
2245 delete[] aColParam.pMinB;
2246 delete[] aColParam.pMaxB;
2249 GDIMetaFile GDIMetaFile::GetMonochromeMtf( const Color& rColor ) const
2251 GDIMetaFile aRet( *this );
2253 ImplColMonoParam aColParam;
2254 ImplBmpMonoParam aBmpParam;
2256 aColParam.aColor = rColor;
2257 aBmpParam.aColor = rColor;
2259 aRet.ImplExchangeColors( ImplColMonoFnc, &aColParam, ImplBmpMonoFnc, &aBmpParam );
2261 return aRet;
2264 BitmapChecksum GDIMetaFile::GetChecksum() const
2266 GDIMetaFile aMtf;
2267 SvMemoryStream aMemStm( 65535, 65535 );
2268 ImplMetaWriteData aWriteData;
2269 SVBT16 aBT16;
2270 SVBT32 aBT32;
2271 BitmapChecksumOctetArray aBCOA;
2272 BitmapChecksum nCrc = 0;
2274 aWriteData.meActualCharSet = aMemStm.GetStreamCharSet();
2275 for( size_t i = 0, nObjCount = GetActionSize(); i < nObjCount; i++ )
2277 MetaAction* pAction = GetAction( i );
2279 switch( pAction->GetType() )
2281 case( MetaActionType::BMP ):
2283 MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
2285 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2286 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2288 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2289 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2291 UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2292 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2294 UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2295 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2297 break;
2299 case( MetaActionType::BMPSCALE ):
2301 MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
2303 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2304 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2306 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2307 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2309 UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2310 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2312 UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2313 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2315 UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2316 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2318 UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2319 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2321 break;
2323 case( MetaActionType::BMPSCALEPART ):
2325 MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
2327 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2328 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2330 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2331 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2333 UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2334 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2336 UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2337 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2339 UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2340 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2342 UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2343 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2345 UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2346 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2348 UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2349 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2351 UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2352 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2354 UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2355 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2357 break;
2359 case( MetaActionType::BMPEX ):
2361 MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
2363 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2364 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2366 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2367 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2369 UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2370 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2372 UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2373 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2375 break;
2377 case( MetaActionType::BMPEXSCALE ):
2379 MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
2381 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2382 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2384 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2385 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2387 UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2388 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2390 UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2391 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2393 UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2394 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2396 UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2397 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2399 break;
2401 case( MetaActionType::BMPEXSCALEPART ):
2403 MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
2405 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2406 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2408 BCToBCOA( pAct->GetBitmapEx().GetChecksum(), aBCOA );
2409 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2411 UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2412 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2414 UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2415 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2417 UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2418 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2420 UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2421 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2423 UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2424 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2426 UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2427 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2429 UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2430 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2432 UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2433 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2435 break;
2437 case( MetaActionType::MASK ):
2439 MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
2441 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2442 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2444 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2445 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2447 UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 );
2448 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2450 UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2451 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2453 UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2454 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2456 break;
2458 case( MetaActionType::MASKSCALE ):
2460 MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
2462 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2463 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2465 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2466 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2468 UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 );
2469 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2471 UInt32ToSVBT32( pAct->GetPoint().X(), aBT32 );
2472 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2474 UInt32ToSVBT32( pAct->GetPoint().Y(), aBT32 );
2475 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2477 UInt32ToSVBT32( pAct->GetSize().Width(), aBT32 );
2478 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2480 UInt32ToSVBT32( pAct->GetSize().Height(), aBT32 );
2481 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2483 break;
2485 case( MetaActionType::MASKSCALEPART ):
2487 MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
2489 ShortToSVBT16( static_cast<sal_uInt16>(pAct->GetType()), aBT16 );
2490 nCrc = vcl_get_checksum( nCrc, aBT16, 2 );
2492 BCToBCOA( pAct->GetBitmap().GetChecksum(), aBCOA );
2493 nCrc = vcl_get_checksum( nCrc, aBCOA, BITMAP_CHECKSUM_SIZE );
2495 UInt32ToSVBT32( pAct->GetColor().GetColor(), aBT32 );
2496 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2498 UInt32ToSVBT32( pAct->GetDestPoint().X(), aBT32 );
2499 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2501 UInt32ToSVBT32( pAct->GetDestPoint().Y(), aBT32 );
2502 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2504 UInt32ToSVBT32( pAct->GetDestSize().Width(), aBT32 );
2505 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2507 UInt32ToSVBT32( pAct->GetDestSize().Height(), aBT32 );
2508 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2510 UInt32ToSVBT32( pAct->GetSrcPoint().X(), aBT32 );
2511 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2513 UInt32ToSVBT32( pAct->GetSrcPoint().Y(), aBT32 );
2514 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2516 UInt32ToSVBT32( pAct->GetSrcSize().Width(), aBT32 );
2517 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2519 UInt32ToSVBT32( pAct->GetSrcSize().Height(), aBT32 );
2520 nCrc = vcl_get_checksum( nCrc, aBT32, 4 );
2522 break;
2524 case MetaActionType::EPS :
2526 MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
2527 nCrc = vcl_get_checksum( nCrc, pAct->GetLink().GetData(), pAct->GetLink().GetDataSize() );
2529 break;
2531 case MetaActionType::CLIPREGION :
2533 MetaClipRegionAction& rAct = static_cast<MetaClipRegionAction&>(*pAction);
2534 const vcl::Region& rRegion = rAct.GetRegion();
2536 if(rRegion.HasPolyPolygonOrB2DPolyPolygon())
2538 // It has shown that this is a possible bottleneck for checksum calculation.
2539 // In worst case a very expensive RegionHandle representation gets created.
2540 // In this case it's cheaper to use the PolyPolygon
2541 const basegfx::B2DPolyPolygon aPolyPolygon(rRegion.GetAsB2DPolyPolygon());
2542 const sal_uInt32 nPolyCount(aPolyPolygon.count());
2543 SVBT64 aSVBT64;
2545 for(sal_uInt32 a(0); a < nPolyCount; a++)
2547 const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(a));
2548 const sal_uInt32 nPointCount(aPolygon.count());
2549 const bool bControl(aPolygon.areControlPointsUsed());
2551 for(sal_uInt32 b(0); b < nPointCount; b++)
2553 const basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(b));
2555 DoubleToSVBT64(aPoint.getX(), aSVBT64);
2556 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2557 DoubleToSVBT64(aPoint.getY(), aSVBT64);
2558 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2560 if(bControl)
2562 if(aPolygon.isPrevControlPointUsed(b))
2564 const basegfx::B2DPoint aCtrl(aPolygon.getPrevControlPoint(b));
2566 DoubleToSVBT64(aCtrl.getX(), aSVBT64);
2567 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2568 DoubleToSVBT64(aCtrl.getY(), aSVBT64);
2569 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2572 if(aPolygon.isNextControlPointUsed(b))
2574 const basegfx::B2DPoint aCtrl(aPolygon.getNextControlPoint(b));
2576 DoubleToSVBT64(aCtrl.getX(), aSVBT64);
2577 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2578 DoubleToSVBT64(aCtrl.getY(), aSVBT64);
2579 nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
2585 sal_uInt8 tmp = (sal_uInt8)rAct.IsClipping();
2586 nCrc = vcl_get_checksum(nCrc, &tmp, 1);
2588 else
2590 pAction->Write( aMemStm, &aWriteData );
2591 nCrc = vcl_get_checksum( nCrc, aMemStm.GetData(), aMemStm.Tell() );
2592 aMemStm.Seek( 0 );
2595 break;
2597 default:
2599 pAction->Write( aMemStm, &aWriteData );
2600 nCrc = vcl_get_checksum( nCrc, aMemStm.GetData(), aMemStm.Tell() );
2601 aMemStm.Seek( 0 );
2603 break;
2607 return nCrc;
2610 sal_uLong GDIMetaFile::GetSizeBytes() const
2612 sal_uLong nSizeBytes = 0;
2614 for( size_t i = 0, nObjCount = GetActionSize(); i < nObjCount; ++i )
2616 MetaAction* pAction = GetAction( i );
2618 // default action size is set to 32 (=> not the exact value)
2619 nSizeBytes += 32;
2621 // add sizes for large action content
2622 switch( pAction->GetType() )
2624 case( MetaActionType::BMP ): nSizeBytes += static_cast<MetaBmpAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2625 case( MetaActionType::BMPSCALE ): nSizeBytes += static_cast<MetaBmpScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2626 case( MetaActionType::BMPSCALEPART ): nSizeBytes += static_cast<MetaBmpScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2628 case( MetaActionType::BMPEX ): nSizeBytes += static_cast<MetaBmpExAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2629 case( MetaActionType::BMPEXSCALE ): nSizeBytes += static_cast<MetaBmpExScaleAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2630 case( MetaActionType::BMPEXSCALEPART ): nSizeBytes += static_cast<MetaBmpExScalePartAction*>( pAction )->GetBitmapEx().GetSizeBytes(); break;
2632 case( MetaActionType::MASK ): nSizeBytes += static_cast<MetaMaskAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2633 case( MetaActionType::MASKSCALE ): nSizeBytes += static_cast<MetaMaskScaleAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2634 case( MetaActionType::MASKSCALEPART ): nSizeBytes += static_cast<MetaMaskScalePartAction*>( pAction )->GetBitmap().GetSizeBytes(); break;
2636 case( MetaActionType::POLYLINE ): nSizeBytes += static_cast<MetaPolyLineAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2637 case( MetaActionType::POLYGON ): nSizeBytes += static_cast<MetaPolygonAction*>( pAction )->GetPolygon().GetSize() * sizeof( Point ); break;
2638 case( MetaActionType::POLYPOLYGON ):
2640 const tools::PolyPolygon& rPolyPoly = static_cast<MetaPolyPolygonAction*>( pAction )->GetPolyPolygon();
2642 for( sal_uInt16 n = 0; n < rPolyPoly.Count(); ++n )
2643 nSizeBytes += ( rPolyPoly[ n ].GetSize() * sizeof( Point ) );
2645 break;
2647 case( MetaActionType::TEXT ): nSizeBytes += static_cast<MetaTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2648 case( MetaActionType::STRETCHTEXT ): nSizeBytes += static_cast<MetaStretchTextAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2649 case( MetaActionType::TEXTRECT ): nSizeBytes += static_cast<MetaTextRectAction*>( pAction )->GetText().getLength() * sizeof( sal_Unicode ); break;
2650 case( MetaActionType::TEXTARRAY ):
2652 MetaTextArrayAction* pTextArrayAction = static_cast<MetaTextArrayAction*>(pAction);
2654 nSizeBytes += ( pTextArrayAction->GetText().getLength() * sizeof( sal_Unicode ) );
2656 if( pTextArrayAction->GetDXArray() )
2657 nSizeBytes += ( pTextArrayAction->GetLen() << 2 );
2659 break;
2660 default: break;
2664 return nSizeBytes;
2667 SvStream& ReadGDIMetaFile( SvStream& rIStm, GDIMetaFile& rGDIMetaFile )
2669 if (rIStm.GetError())
2671 SAL_WARN("vcl.gdi", "Stream error: " << rIStm.GetError());
2672 return rIStm;
2675 sal_uLong nStmPos = rIStm.Tell();
2676 SvStreamEndian nOldFormat = rIStm.GetEndian();
2678 rIStm.SetEndian( SvStreamEndian::LITTLE );
2682 char aId[7];
2683 aId[0] = 0;
2684 aId[6] = 0;
2685 rIStm.ReadBytes( aId, 6 );
2687 if ( !strcmp( aId, "VCLMTF" ) )
2689 // new format
2690 sal_uInt32 nStmCompressMode = 0;
2691 sal_uInt32 nCount = 0;
2692 std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rIStm, StreamMode::READ ));
2694 rIStm.ReadUInt32( nStmCompressMode );
2695 ReadMapMode( rIStm, rGDIMetaFile.m_aPrefMapMode );
2696 ReadPair( rIStm, rGDIMetaFile.m_aPrefSize );
2697 rIStm.ReadUInt32( nCount );
2699 pCompat.reset(); // destructor writes stuff into the header
2701 ImplMetaReadData aReadData;
2702 aReadData.meActualCharSet = rIStm.GetStreamCharSet();
2704 for( sal_uInt32 nAction = 0UL; ( nAction < nCount ) && !rIStm.IsEof(); nAction++ )
2706 MetaAction* pAction = MetaAction::ReadMetaAction( rIStm, &aReadData );
2707 if( pAction )
2709 if (pAction->GetType() == MetaActionType::COMMENT)
2711 MetaCommentAction* pCommentAct = static_cast<MetaCommentAction*>(pAction);
2712 if ( pCommentAct->GetComment() == "EMF_PLUS" )
2713 rGDIMetaFile.UseCanvas( true );
2715 rGDIMetaFile.AddAction( pAction );
2719 else
2721 // to avoid possible compiler optimizations => new/delete
2722 rIStm.Seek( nStmPos );
2723 delete( new SVMConverter( rIStm, rGDIMetaFile, CONVERT_FROM_SVM1 ) );
2726 catch (...)
2728 SAL_WARN("vcl", "GDIMetaFile exception during load");
2729 rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
2732 // check for errors
2733 if( rIStm.GetError() )
2735 rGDIMetaFile.Clear();
2736 rIStm.Seek( nStmPos );
2739 rIStm.SetEndian( nOldFormat );
2740 return rIStm;
2743 SvStream& WriteGDIMetaFile( SvStream& rOStm, const GDIMetaFile& rGDIMetaFile )
2745 if( !rOStm.GetError() )
2747 static const char* pEnableSVM1 = getenv( "SAL_ENABLE_SVM1" );
2748 static const bool bNoSVM1 = (nullptr == pEnableSVM1 ) || ( '0' == *pEnableSVM1 );
2750 if( bNoSVM1 || rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 )
2752 const_cast< GDIMetaFile& >( rGDIMetaFile ).Write( rOStm );
2754 else
2756 delete( new SVMConverter( rOStm, const_cast< GDIMetaFile& >( rGDIMetaFile ), CONVERT_TO_SVM1 ) );
2759 #ifdef DEBUG
2760 if( !bNoSVM1 && rOStm.GetVersion() < SOFFICE_FILEFORMAT_50 )
2762 SAL_WARN( "vcl", "GDIMetaFile would normally be written in old SVM1 format by this call. "
2763 "The current implementation always writes in VCLMTF format. "
2764 "Please set environment variable SAL_ENABLE_SVM1 to '1' to reenable old behavior" );
2766 #endif // DEBUG
2769 return rOStm;
2772 SvStream& GDIMetaFile::Read( SvStream& rIStm )
2774 Clear();
2775 ReadGDIMetaFile( rIStm, *this );
2777 return rIStm;
2780 SvStream& GDIMetaFile::Write( SvStream& rOStm )
2782 VersionCompat* pCompat;
2783 const SvStreamCompressFlags nStmCompressMode = rOStm.GetCompressMode();
2784 SvStreamEndian nOldFormat = rOStm.GetEndian();
2786 rOStm.SetEndian( SvStreamEndian::LITTLE );
2787 rOStm.WriteBytes( "VCLMTF", 6 );
2789 pCompat = new VersionCompat( rOStm, StreamMode::WRITE, 1 );
2791 rOStm.WriteUInt32( static_cast<sal_uInt32>(nStmCompressMode) );
2792 WriteMapMode( rOStm, m_aPrefMapMode );
2793 WritePair( rOStm, m_aPrefSize );
2794 rOStm.WriteUInt32( GetActionSize() );
2796 delete pCompat;
2798 ImplMetaWriteData aWriteData;
2800 aWriteData.meActualCharSet = rOStm.GetStreamCharSet();
2802 MetaAction* pAct = FirstAction();
2803 while ( pAct )
2805 pAct->Write( rOStm, &aWriteData );
2806 pAct = NextAction();
2809 rOStm.SetEndian( nOldFormat );
2811 return rOStm;
2814 bool GDIMetaFile::CreateThumbnail(BitmapEx& rBitmapEx, BmpConversion eColorConversion, BmpScaleFlag nScaleFlag) const
2816 // initialization seems to be complicated but is used to avoid rounding errors
2817 ScopedVclPtrInstance< VirtualDevice > aVDev;
2818 const Point aNullPt;
2819 const Point aTLPix( aVDev->LogicToPixel( aNullPt, GetPrefMapMode() ) );
2820 const Point aBRPix( aVDev->LogicToPixel( Point( GetPrefSize().Width() - 1, GetPrefSize().Height() - 1 ), GetPrefMapMode() ) );
2821 Size aDrawSize( aVDev->LogicToPixel( GetPrefSize(), GetPrefMapMode() ) );
2822 Size aSizePix( labs( aBRPix.X() - aTLPix.X() ) + 1, labs( aBRPix.Y() - aTLPix.Y() ) + 1 );
2823 sal_uInt32 nMaximumExtent = 256;
2825 if (!rBitmapEx.IsEmpty())
2826 rBitmapEx.SetEmpty();
2828 // determine size that has the same aspect ratio as image size and
2829 // fits into the rectangle determined by nMaximumExtent
2830 if ( aSizePix.Width() && aSizePix.Height()
2831 && ( sal::static_int_cast< unsigned long >(aSizePix.Width()) >
2832 nMaximumExtent ||
2833 sal::static_int_cast< unsigned long >(aSizePix.Height()) >
2834 nMaximumExtent ) )
2836 const Size aOldSizePix( aSizePix );
2837 double fWH = static_cast< double >( aSizePix.Width() ) / aSizePix.Height();
2839 if ( fWH <= 1.0 )
2841 aSizePix.Width() = FRound( nMaximumExtent * fWH );
2842 aSizePix.Height() = nMaximumExtent;
2844 else
2846 aSizePix.Width() = nMaximumExtent;
2847 aSizePix.Height() = FRound( nMaximumExtent / fWH );
2850 aDrawSize.Width() = FRound( ( static_cast< double >( aDrawSize.Width() ) * aSizePix.Width() ) / aOldSizePix.Width() );
2851 aDrawSize.Height() = FRound( ( static_cast< double >( aDrawSize.Height() ) * aSizePix.Height() ) / aOldSizePix.Height() );
2854 // draw image(s) into VDev and get resulting image
2855 // do it 4x larger to be able to scale it down & get beautiful antialias
2856 Size aAntialiasSize(aSizePix.Width() * 4, aSizePix.Height() * 4);
2857 if (aVDev->SetOutputSizePixel(aAntialiasSize))
2859 // antialias: provide 4x larger size, and then scale down the result
2860 Size aAntialias(aDrawSize.Width() * 4, aDrawSize.Height() * 4);
2862 // draw metafile into VDev
2863 Point aBackPosPix;
2864 const_cast<GDIMetaFile *>(this)->WindStart();
2865 const_cast<GDIMetaFile *>(this)->Play(aVDev.get(), aBackPosPix, aAntialias);
2867 // get paint bitmap
2868 Bitmap aBitmap( aVDev->GetBitmap( aNullPt, aVDev->GetOutputSizePixel() ) );
2870 // scale down the image to the desired size - use the input scaler for the scaling operation
2871 aBitmap.Scale(aDrawSize, nScaleFlag);
2873 // convert to desired bitmap color format
2874 aBitmap.Convert(eColorConversion);
2876 rBitmapEx = BitmapEx(aBitmap);
2879 return !rBitmapEx.IsEmpty();
2882 void GDIMetaFile::UseCanvas( bool _bUseCanvas )
2884 m_bUseCanvas = _bUseCanvas;
2887 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */