Bump version to 5.0-14
[LibreOffice.git] / svtools / source / graphic / grfmgr.cxx
blob5dc71286839b33814a5643e658a82e175dc8218f
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 <sal/config.h>
22 #include <algorithm>
24 #include <officecfg/Office/Common.hxx>
25 #include <tools/vcompat.hxx>
26 #include <tools/fract.hxx>
27 #include <tools/helpers.hxx>
28 #include <unotools/ucbstreamhelper.hxx>
29 #include <unotools/localfilehelper.hxx>
30 #include <unotools/tempfile.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/cvtgrf.hxx>
33 #include <vcl/metaact.hxx>
34 #include <vcl/virdev.hxx>
35 #include <svtools/grfmgr.hxx>
37 #include <com/sun/star/container/XNameContainer.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <boost/scoped_ptr.hpp>
41 using com::sun::star::uno::Reference;
42 using com::sun::star::uno::XInterface;
43 using com::sun::star::uno::UNO_QUERY;
44 using com::sun::star::uno::Sequence;
45 using com::sun::star::container::XNameContainer;
46 using com::sun::star::beans::XPropertySet;
48 GraphicManager* GraphicObject::mpGlobalMgr = NULL;
50 struct GrfSimpleCacheObj
52 Graphic maGraphic;
53 GraphicAttr maAttr;
55 GrfSimpleCacheObj( const Graphic& rGraphic, const GraphicAttr& rAttr ) :
56 maGraphic( rGraphic ), maAttr( rAttr ) {}
59 TYPEINIT1_AUTOFACTORY( GraphicObject, SvDataCopyStream );
61 // unique increasing ID for being able to detect the GraphicObject with the
62 // oldest last data changes
63 static sal_uLong aIncrementingTimeOfLastDataChange = 1;
65 void GraphicObject::ImplAfterDataChange()
67 // set unique timestamp ID of last data change
68 mnDataChangeTimeStamp = aIncrementingTimeOfLastDataChange++;
70 // check memory footprint of all GraphicObjects managed and evtl. take action
71 if (mpMgr)
72 mpMgr->ImplCheckSizeOfSwappedInGraphics(this);
75 GraphicObject::GraphicObject( const GraphicManager* pMgr ) :
76 maLink (),
77 maUserData ()
79 ImplConstruct();
80 ImplAssignGraphicData();
81 ImplSetGraphicManager( pMgr );
84 GraphicObject::GraphicObject( const Graphic& rGraphic, const GraphicManager* pMgr ) :
85 maGraphic ( rGraphic ),
86 maLink (),
87 maUserData ()
89 ImplConstruct();
90 ImplAssignGraphicData();
91 ImplSetGraphicManager( pMgr );
94 GraphicObject::GraphicObject( const GraphicObject& rGraphicObj, const GraphicManager* pMgr ) :
95 SvDataCopyStream(),
96 maGraphic ( rGraphicObj.GetGraphic() ),
97 maAttr ( rGraphicObj.maAttr ),
98 maLink ( rGraphicObj.maLink ),
99 maUserData ( rGraphicObj.maUserData )
101 ImplConstruct();
102 ImplAssignGraphicData();
103 ImplSetGraphicManager( pMgr, NULL, &rGraphicObj );
104 if( rGraphicObj.HasUserData() && rGraphicObj.IsSwappedOut() )
105 SetSwapState();
108 GraphicObject::GraphicObject( const OString& rUniqueID, const GraphicManager* pMgr ) :
109 maLink (),
110 maUserData ()
112 ImplConstruct();
114 // assign default properties
115 ImplAssignGraphicData();
117 ImplSetGraphicManager( pMgr, &rUniqueID );
119 // update properties
120 ImplAssignGraphicData();
123 GraphicObject::~GraphicObject()
125 if( mpMgr )
127 mpMgr->ImplUnregisterObj( *this );
129 if( ( mpMgr == mpGlobalMgr ) && !mpGlobalMgr->ImplHasObjects() )
130 delete mpGlobalMgr, mpGlobalMgr = NULL;
133 delete mpSwapOutTimer;
134 delete mpSwapStreamHdl;
135 delete mpSimpleCache;
138 void GraphicObject::ImplConstruct()
140 mpMgr = NULL;
141 mpSwapStreamHdl = NULL;
142 mpSwapOutTimer = NULL;
143 mpSimpleCache = NULL;
144 mnAnimationLoopCount = 0;
145 mbAutoSwapped = false;
146 mbIsInSwapIn = false;
147 mbIsInSwapOut = false;
149 // Init with a unique, increasing ID
150 mnDataChangeTimeStamp = aIncrementingTimeOfLastDataChange++;
153 void GraphicObject::ImplAssignGraphicData()
155 maPrefSize = maGraphic.GetPrefSize();
156 maPrefMapMode = maGraphic.GetPrefMapMode();
157 mnSizeBytes = maGraphic.GetSizeBytes();
158 meType = maGraphic.GetType();
159 mbTransparent = maGraphic.IsTransparent();
160 mbAlpha = maGraphic.IsAlpha();
161 mbAnimated = maGraphic.IsAnimated();
162 mbEPS = maGraphic.IsEPS();
163 mnAnimationLoopCount = ( mbAnimated ? maGraphic.GetAnimationLoopCount() : 0 );
165 // Handle evtl. needed AfterDataChanges
166 ImplAfterDataChange();
169 void GraphicObject::ImplSetGraphicManager( const GraphicManager* pMgr, const OString* pID, const GraphicObject* pCopyObj )
171 if( !mpMgr || ( pMgr != mpMgr ) )
173 if( !pMgr && mpMgr && ( mpMgr == mpGlobalMgr ) )
174 return;
175 else
177 if( mpMgr )
179 mpMgr->ImplUnregisterObj( *this );
181 if( ( mpMgr == mpGlobalMgr ) && !mpGlobalMgr->ImplHasObjects() )
182 delete mpGlobalMgr, mpGlobalMgr = NULL;
185 if( !pMgr )
187 if( !mpGlobalMgr )
189 mpGlobalMgr = new GraphicManager(
190 (officecfg::Office::Common::Cache::GraphicManager::
191 TotalCacheSize::get()),
192 (officecfg::Office::Common::Cache::GraphicManager::
193 ObjectCacheSize::get()));
194 mpGlobalMgr->SetCacheTimeout(
195 officecfg::Office::Common::Cache::GraphicManager::
196 ObjectReleaseTime::get());
199 mpMgr = mpGlobalMgr;
201 else
202 mpMgr = const_cast<GraphicManager*>(pMgr);
204 mpMgr->ImplRegisterObj( *this, maGraphic, pID, pCopyObj );
209 void GraphicObject::ImplAutoSwapIn()
211 if( IsSwappedOut() )
214 mbIsInSwapIn = true;
216 if( maGraphic.SwapIn() )
217 mbAutoSwapped = false;
218 else
220 SvStream* pStream = GetSwapStream();
222 if( GRFMGR_AUTOSWAPSTREAM_NONE != pStream )
224 if( GRFMGR_AUTOSWAPSTREAM_LINK == pStream )
226 if( HasLink() )
228 OUString aURLStr;
230 if( ::utl::LocalFileHelper::ConvertPhysicalNameToURL( GetLink(), aURLStr ) )
232 boost::scoped_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( aURLStr, StreamMode::READ ));
234 if( pIStm )
236 ReadGraphic( *pIStm, maGraphic );
237 mbAutoSwapped = ( maGraphic.GetType() != GRAPHIC_NONE );
242 else if( GRFMGR_AUTOSWAPSTREAM_TEMP == pStream )
243 mbAutoSwapped = !maGraphic.SwapIn();
244 else if( GRFMGR_AUTOSWAPSTREAM_LOADED == pStream )
245 mbAutoSwapped = maGraphic.IsSwapOut();
246 else
248 mbAutoSwapped = !maGraphic.SwapIn( pStream );
249 delete pStream;
252 else
254 DBG_ASSERT( ( GRAPHIC_NONE == meType ) || ( GRAPHIC_DEFAULT == meType ),
255 "GraphicObject::ImplAutoSwapIn: could not get stream to swap in graphic! (=>KA)" );
259 mbIsInSwapIn = false;
261 if( !mbAutoSwapped && mpMgr )
262 mpMgr->ImplGraphicObjectWasSwappedIn( *this );
264 ImplAssignGraphicData();
268 bool GraphicObject::ImplGetCropParams( OutputDevice* pOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr,
269 tools::PolyPolygon& rClipPolyPoly, bool& bRectClipRegion ) const
271 bool bRet = false;
273 if( GetType() != GRAPHIC_NONE )
275 Polygon aClipPoly( Rectangle( rPt, rSz ) );
276 const sal_uInt16 nRot10 = pAttr->GetRotation() % 3600;
277 const Point aOldOrigin( rPt );
278 const MapMode aMap100( MAP_100TH_MM );
279 Size aSize100;
280 long nTotalWidth, nTotalHeight;
282 if( nRot10 )
284 aClipPoly.Rotate( rPt, nRot10 );
285 bRectClipRegion = false;
287 else
288 bRectClipRegion = true;
290 rClipPolyPoly = aClipPoly;
292 if( maGraphic.GetPrefMapMode() == MAP_PIXEL )
293 aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 );
294 else
296 MapMode m(maGraphic.GetPrefMapMode());
297 aSize100 = pOut->LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 );
300 nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop();
301 nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop();
303 if( aSize100.Width() > 0 && aSize100.Height() > 0 && nTotalWidth > 0 && nTotalHeight > 0 )
305 double fScale = (double) aSize100.Width() / nTotalWidth;
306 const long nNewLeft = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * fScale );
307 const long nNewRight = nNewLeft + FRound( aSize100.Width() * fScale ) - 1;
309 fScale = (double) rSz.Width() / aSize100.Width();
310 rPt.X() += FRound( nNewLeft * fScale );
311 rSz.Width() = FRound( ( nNewRight - nNewLeft + 1 ) * fScale );
313 fScale = (double) aSize100.Height() / nTotalHeight;
314 const long nNewTop = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * fScale );
315 const long nNewBottom = nNewTop + FRound( aSize100.Height() * fScale ) - 1;
317 fScale = (double) rSz.Height() / aSize100.Height();
318 rPt.Y() += FRound( nNewTop * fScale );
319 rSz.Height() = FRound( ( nNewBottom - nNewTop + 1 ) * fScale );
321 if( nRot10 )
323 Polygon aOriginPoly( 1 );
325 aOriginPoly[ 0 ] = rPt;
326 aOriginPoly.Rotate( aOldOrigin, nRot10 );
327 rPt = aOriginPoly[ 0 ];
330 bRet = true;
334 return bRet;
337 GraphicObject& GraphicObject::operator=( const GraphicObject& rGraphicObj )
339 if( &rGraphicObj != this )
341 mpMgr->ImplUnregisterObj( *this );
343 delete mpSwapStreamHdl, mpSwapStreamHdl = NULL;
344 delete mpSimpleCache, mpSimpleCache = NULL;
346 maGraphic = rGraphicObj.GetGraphic();
347 maAttr = rGraphicObj.maAttr;
348 maLink = rGraphicObj.maLink;
349 maUserData = rGraphicObj.maUserData;
350 ImplAssignGraphicData();
351 mbAutoSwapped = false;
352 mpMgr = rGraphicObj.mpMgr;
353 mpMgr->ImplRegisterObj( *this, maGraphic, NULL, &rGraphicObj );
354 if( rGraphicObj.HasUserData() && rGraphicObj.IsSwappedOut() )
355 SetSwapState();
358 return *this;
361 bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const
363 return( ( rGraphicObj.maGraphic == maGraphic ) &&
364 ( rGraphicObj.maAttr == maAttr ) &&
365 ( rGraphicObj.GetLink() == GetLink() ) );
368 void GraphicObject::Load( SvStream& rIStm )
370 ReadGraphicObject( rIStm, *this );
373 void GraphicObject::Save( SvStream& rOStm )
375 WriteGraphicObject( rOStm, *this );
378 void GraphicObject::Assign( const SvDataCopyStream& rCopyStream )
380 *this = static_cast<const GraphicObject&>(rCopyStream);
383 OString GraphicObject::GetUniqueID() const
385 if ( !IsInSwapIn() && IsEPS() )
386 const_cast<GraphicObject*>(this)->FireSwapInRequest();
388 OString aRet;
390 if( mpMgr )
391 aRet = mpMgr->ImplGetUniqueID( *this );
393 return aRet;
396 SvStream* GraphicObject::GetSwapStream() const
398 if( HasSwapStreamHdl() )
399 return reinterpret_cast<SvStream*>( mpSwapStreamHdl->Call( const_cast<void*>(static_cast<const void*>(this)) ) );
400 else
401 return GRFMGR_AUTOSWAPSTREAM_NONE;
404 void GraphicObject::SetAttr( const GraphicAttr& rAttr )
406 maAttr = rAttr;
408 if( mpSimpleCache && ( mpSimpleCache->maAttr != rAttr ) )
409 delete mpSimpleCache, mpSimpleCache = NULL;
412 void GraphicObject::SetLink()
414 maLink.clear();
417 void GraphicObject::SetLink( const OUString& rLink )
419 maLink = rLink;
422 void GraphicObject::SetUserData()
424 maUserData.clear();
427 void GraphicObject::SetUserData( const OUString& rUserData )
429 maUserData = rUserData;
430 if( !rUserData.isEmpty() )
431 SetSwapState();
434 void GraphicObject::SetSwapStreamHdl()
436 if( mpSwapStreamHdl )
438 delete mpSwapOutTimer, mpSwapOutTimer = NULL;
439 delete mpSwapStreamHdl, mpSwapStreamHdl = NULL;
443 static sal_uInt32 GetCacheTimeInMs()
445 const sal_uInt32 nSeconds =
446 officecfg::Office::Common::Cache::GraphicManager::ObjectReleaseTime::get(
447 comphelper::getProcessComponentContext());
449 return nSeconds * 1000;
452 void GraphicObject::SetSwapStreamHdl(const Link<>& rHdl)
454 delete mpSwapStreamHdl, mpSwapStreamHdl = new Link<>( rHdl );
456 sal_uInt32 const nSwapOutTimeout(GetCacheTimeInMs());
457 if( nSwapOutTimeout )
459 if( !mpSwapOutTimer )
461 mpSwapOutTimer = new Timer;
462 mpSwapOutTimer->SetTimeoutHdl( LINK( this, GraphicObject, ImplAutoSwapOutHdl ) );
465 mpSwapOutTimer->SetTimeout( nSwapOutTimeout );
466 mpSwapOutTimer->Start();
468 else
469 delete mpSwapOutTimer, mpSwapOutTimer = NULL;
472 void GraphicObject::FireSwapInRequest()
474 ImplAutoSwapIn();
477 void GraphicObject::FireSwapOutRequest()
479 ImplAutoSwapOutHdl( NULL );
482 void GraphicObject::GraphicManagerDestroyed()
484 // we're alive, but our manager doesn't live anymore ==> connect to default manager
485 mpMgr = NULL;
486 ImplSetGraphicManager( NULL );
489 bool GraphicObject::IsCached( OutputDevice* pOut, const Point& rPt, const Size& rSz,
490 const GraphicAttr* pAttr, GraphicManagerDrawFlags nFlags ) const
492 bool bRet;
494 if( nFlags & GraphicManagerDrawFlags::CACHED )
496 Point aPt( rPt );
497 Size aSz( rSz );
498 if ( pAttr && pAttr->IsCropped() )
500 tools::PolyPolygon aClipPolyPoly;
501 bool bRectClip;
502 ImplGetCropParams( pOut, aPt, aSz, pAttr, aClipPolyPoly, bRectClip );
504 bRet = mpMgr->IsInCache( pOut, aPt, aSz, *this, ( pAttr ? *pAttr : GetAttr() ) );
506 else
507 bRet = false;
509 return bRet;
512 bool GraphicObject::Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz,
513 const GraphicAttr* pAttr, GraphicManagerDrawFlags nFlags )
515 GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
516 Point aPt( rPt );
517 Size aSz( rSz );
518 const DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
519 bool bCropped = aAttr.IsCropped();
520 bool bCached = false;
521 bool bRet;
523 // #i29534# Provide output rects for PDF writer
524 Rectangle aCropRect;
526 if( !( GraphicManagerDrawFlags::USE_DRAWMODE_SETTINGS & nFlags ) )
527 pOut->SetDrawMode( nOldDrawMode & ~DrawModeFlags( DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient ) );
529 // mirrored horizontically
530 if( aSz.Width() < 0L )
532 aPt.X() += aSz.Width() + 1;
533 aSz.Width() = -aSz.Width();
534 aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Horizontal );
537 // mirrored vertically
538 if( aSz.Height() < 0L )
540 aPt.Y() += aSz.Height() + 1;
541 aSz.Height() = -aSz.Height();
542 aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Vertical );
545 if( bCropped )
547 tools::PolyPolygon aClipPolyPoly;
548 bool bRectClip;
549 const bool bCrop = ImplGetCropParams( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip );
551 pOut->Push( PushFlags::CLIPREGION );
553 if( bCrop )
555 if( bRectClip )
557 // #i29534# Store crop rect for later forwarding to
558 // PDF writer
559 aCropRect = aClipPolyPoly.GetBoundRect();
560 pOut->IntersectClipRegion( aCropRect );
562 else
564 pOut->IntersectClipRegion(vcl::Region(aClipPolyPoly));
569 bRet = mpMgr->DrawObj( pOut, aPt, aSz, *this, aAttr, nFlags, bCached );
571 if( bCropped )
572 pOut->Pop();
574 pOut->SetDrawMode( nOldDrawMode );
576 // #i29534# Moved below OutDev restoration, to avoid multiple swap-ins
577 // (code above needs to call GetGraphic twice)
578 if( bCached )
580 if( mpSwapOutTimer )
581 mpSwapOutTimer->Start();
582 else
583 FireSwapOutRequest();
586 return bRet;
589 bool GraphicObject::DrawTiled( OutputDevice* pOut, const Rectangle& rArea, const Size& rSize,
590 const Size& rOffset, const GraphicAttr* pAttr, GraphicManagerDrawFlags nFlags, int nTileCacheSize1D )
592 if( pOut == NULL || rSize.Width() == 0 || rSize.Height() == 0 )
593 return false;
595 const MapMode aOutMapMode( pOut->GetMapMode() );
596 const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() );
597 // #106258# Clamp size to 1 for zero values. This is okay, since
598 // logical size of zero is handled above already
599 const Size aOutTileSize( ::std::max( 1L, pOut->LogicToPixel( rSize, aOutMapMode ).Width() ),
600 ::std::max( 1L, pOut->LogicToPixel( rSize, aOutMapMode ).Height() ) );
602 //#i69780 clip final tile size to a sane max size
603 while (((sal_Int64)rSize.Width() * nTileCacheSize1D) > SAL_MAX_UINT16)
604 nTileCacheSize1D /= 2;
605 while (((sal_Int64)rSize.Height() * nTileCacheSize1D) > SAL_MAX_UINT16)
606 nTileCacheSize1D /= 2;
608 return ImplDrawTiled( pOut, rArea, aOutTileSize, rOffset, pAttr, nFlags, nTileCacheSize1D );
611 bool GraphicObject::StartAnimation( OutputDevice* pOut, const Point& rPt, const Size& rSz,
612 long nExtraData, const GraphicAttr* pAttr, GraphicManagerDrawFlags /*nFlags*/,
613 OutputDevice* pFirstFrameOutDev )
615 bool bRet = false;
617 GetGraphic();
619 if( !IsSwappedOut() )
621 const GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
623 if( mbAnimated )
625 Point aPt( rPt );
626 Size aSz( rSz );
627 bool bCropped = aAttr.IsCropped();
629 if( bCropped )
631 tools::PolyPolygon aClipPolyPoly;
632 bool bRectClip;
633 const bool bCrop = ImplGetCropParams( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip );
635 pOut->Push( PushFlags::CLIPREGION );
637 if( bCrop )
639 if( bRectClip )
640 pOut->IntersectClipRegion( aClipPolyPoly.GetBoundRect() );
641 else
642 pOut->IntersectClipRegion(vcl::Region(aClipPolyPoly));
646 if( !mpSimpleCache || ( mpSimpleCache->maAttr != aAttr ) || pFirstFrameOutDev )
648 if( mpSimpleCache )
649 delete mpSimpleCache;
651 mpSimpleCache = new GrfSimpleCacheObj( GetTransformedGraphic( &aAttr ), aAttr );
652 mpSimpleCache->maGraphic.SetAnimationNotifyHdl( GetAnimationNotifyHdl() );
655 mpSimpleCache->maGraphic.StartAnimation( pOut, aPt, aSz, nExtraData, pFirstFrameOutDev );
657 if( bCropped )
658 pOut->Pop();
660 bRet = true;
662 else
663 bRet = Draw( pOut, rPt, rSz, &aAttr, GraphicManagerDrawFlags::STANDARD );
666 return bRet;
669 void GraphicObject::StopAnimation( OutputDevice* pOut, long nExtraData )
671 if( mpSimpleCache )
672 mpSimpleCache->maGraphic.StopAnimation( pOut, nExtraData );
675 const Graphic& GraphicObject::GetGraphic() const
677 GraphicObject *pThis = const_cast<GraphicObject*>(this);
678 (void)pThis->SwapIn();
680 //fdo#50697 If we've been asked to provide the graphic, then reset
681 //the cache timeout to start from now and not remain at the
682 //time of creation
683 pThis->restartSwapOutTimer();
685 return maGraphic;
688 void GraphicObject::SetGraphic( const Graphic& rGraphic, const GraphicObject* pCopyObj )
690 mpMgr->ImplUnregisterObj( *this );
692 if( mpSwapOutTimer )
693 mpSwapOutTimer->Stop();
695 maGraphic = rGraphic;
696 mbAutoSwapped = false;
697 ImplAssignGraphicData();
698 maLink.clear();
699 delete mpSimpleCache, mpSimpleCache = NULL;
701 mpMgr->ImplRegisterObj( *this, maGraphic, 0, pCopyObj);
703 if( mpSwapOutTimer )
704 mpSwapOutTimer->Start();
709 void GraphicObject::SetGraphic( const Graphic& rGraphic, const OUString& rLink )
711 SetGraphic( rGraphic );
712 maLink = rLink;
715 Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const
717 // #104550# Extracted from svx/source/svdraw/svdograf.cxx
718 Graphic aTransGraphic( GetGraphic() );
719 const GraphicType eType = GetType();
720 const Size aSrcSize( aTransGraphic.GetPrefSize() );
722 // #104115# Convert the crop margins to graphic object mapmode
723 const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() );
724 const MapMode aMap100( MAP_100TH_MM );
726 Size aCropLeftTop;
727 Size aCropRightBottom;
729 if( GRAPHIC_GDIMETAFILE == eType )
731 GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() );
733 if( aMapGraph == MAP_PIXEL )
735 // crops are in 1/100th mm -> to aMapGraph -> to MAP_PIXEL
736 aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
737 Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
738 aMap100);
739 aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
740 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
741 aMap100);
743 else
745 // crops are in GraphicObject units -> to aMapGraph
746 aCropLeftTop = OutputDevice::LogicToLogic(
747 Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
748 aMap100,
749 aMapGraph);
750 aCropRightBottom = OutputDevice::LogicToLogic(
751 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
752 aMap100,
753 aMapGraph);
756 // #104115# If the metafile is cropped, give it a special
757 // treatment: clip against the remaining area, scale up such
758 // that this area later fills the desired size, and move the
759 // origin to the upper left edge of that area.
760 if( rAttr.IsCropped() )
762 const MapMode aMtfMapMode( aMtf.GetPrefMapMode() );
764 Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(),
765 aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(),
766 aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(),
767 aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() );
769 // #104115# To correctly crop rotated metafiles, clip by view rectangle
770 aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 );
772 // #104115# To crop the metafile, scale larger than the output rectangle
773 aMtf.Scale( (double)rDestSize.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()),
774 (double)rDestSize.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) );
776 // #104115# Adapt the pref size by hand (scale changes it
777 // proportionally, but we want it to be smaller than the
778 // former size, to crop the excess out)
779 aMtf.SetPrefSize( Size( (long)((double)rDestSize.Width() * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5),
780 (long)((double)rDestSize.Height() * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) );
782 // #104115# Adapt the origin of the new mapmode, such that it
783 // is shifted to the place where the cropped output starts
784 Point aNewOrigin( (long)((double)aMtfMapMode.GetOrigin().X() + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5),
785 (long)((double)aMtfMapMode.GetOrigin().Y() + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) );
786 MapMode aNewMap( rDestMap );
787 aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) );
788 aMtf.SetPrefMapMode( aNewMap );
790 else
792 aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) );
793 aMtf.SetPrefMapMode( rDestMap );
796 aTransGraphic = aMtf;
798 else if( GRAPHIC_BITMAP == eType )
800 BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() );
801 Rectangle aCropRect;
803 // convert crops to pixel
804 if(rAttr.IsCropped())
806 if( aMapGraph == MAP_PIXEL )
808 // crops are in 1/100th mm -> to MAP_PIXEL
809 aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
810 Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
811 aMap100);
812 aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
813 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
814 aMap100);
816 else
818 // crops are in GraphicObject units -> to MAP_PIXEL
819 aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
820 Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
821 aMapGraph);
822 aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
823 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
824 aMapGraph);
827 // convert from prefmapmode to pixel
828 Size aSrcSizePixel(
829 Application::GetDefaultDevice()->LogicToPixel(
830 aSrcSize,
831 aMapGraph));
833 if(rAttr.IsCropped()
834 && (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height())
835 && aSrcSizePixel.Width())
837 // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
838 // and it's internal size (aTransGraphic.GetPrefSize()) is different from it's real pixel size.
839 // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
840 // existing cropping is calculated based on this logic values already.
841 // aBitmapEx.Scale(aSrcSizePixel);
843 // another possibility is to adapt the values created so far with a factor; this
844 // will keep the original Bitmap untouched and thus quality will not change
845 // caution: convert to double first, else pretty big errors may occur
846 const double fFactorX((double)aBitmapEx.GetSizePixel().Width() / aSrcSizePixel.Width());
847 const double fFactorY((double)aBitmapEx.GetSizePixel().Height() / aSrcSizePixel.Height());
849 aCropLeftTop.Width() = basegfx::fround(aCropLeftTop.Width() * fFactorX);
850 aCropLeftTop.Height() = basegfx::fround(aCropLeftTop.Height() * fFactorY);
851 aCropRightBottom.Width() = basegfx::fround(aCropRightBottom.Width() * fFactorX);
852 aCropRightBottom.Height() = basegfx::fround(aCropRightBottom.Height() * fFactorY);
854 aSrcSizePixel = aBitmapEx.GetSizePixel();
857 // setup crop rectangle in pixel
858 aCropRect = Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(),
859 aSrcSizePixel.Width() - aCropRightBottom.Width(),
860 aSrcSizePixel.Height() - aCropRightBottom.Height() );
863 // #105641# Also crop animations
864 if( aTransGraphic.IsAnimated() )
866 sal_uInt16 nFrame;
867 Animation aAnim( aTransGraphic.GetAnimation() );
869 for( nFrame=0; nFrame<aAnim.Count(); ++nFrame )
871 AnimationBitmap aAnimBmp( aAnim.Get( nFrame ) );
873 if( !aCropRect.IsInside( Rectangle(aAnimBmp.aPosPix, aAnimBmp.aSizePix) ) )
875 // setup actual cropping (relative to frame position)
876 Rectangle aCropRectRel( aCropRect );
877 aCropRectRel.Move( -aAnimBmp.aPosPix.X(),
878 -aAnimBmp.aPosPix.Y() );
880 // cropping affects this frame, apply it then
881 // do _not_ apply enlargement, this is done below
882 ImplTransformBitmap( aAnimBmp.aBmpEx, rAttr, Size(), Size(),
883 aCropRectRel, rDestSize, false );
885 aAnim.Replace( aAnimBmp, nFrame );
887 // else: bitmap completely within crop area,
888 // i.e. nothing is cropped away
891 // now, apply enlargement (if any) through global animation size
892 if( aCropLeftTop.Width() < 0 ||
893 aCropLeftTop.Height() < 0 ||
894 aCropRightBottom.Width() < 0 ||
895 aCropRightBottom.Height() < 0 )
897 Size aNewSize( aAnim.GetDisplaySizePixel() );
898 aNewSize.Width() += aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0;
899 aNewSize.Width() += aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0;
900 aNewSize.Height() += aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0;
901 aNewSize.Height() += aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0;
902 aAnim.SetDisplaySizePixel( aNewSize );
905 // if topleft has changed, we must move all frames to the
906 // right and bottom, resp.
907 if( aCropLeftTop.Width() < 0 ||
908 aCropLeftTop.Height() < 0 )
910 Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0,
911 aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
913 for( nFrame=0; nFrame<aAnim.Count(); ++nFrame )
915 AnimationBitmap aAnimBmp( aAnim.Get( nFrame ) );
917 aAnimBmp.aPosPix += aPosOffset;
919 aAnim.Replace( aAnimBmp, nFrame );
923 aTransGraphic = aAnim;
925 else
927 ImplTransformBitmap( aBitmapEx, rAttr, aCropLeftTop, aCropRightBottom,
928 aCropRect, rDestSize, true );
930 aTransGraphic = aBitmapEx;
933 aTransGraphic.SetPrefSize( rDestSize );
934 aTransGraphic.SetPrefMapMode( rDestMap );
937 GraphicObject aGrfObj( aTransGraphic );
938 aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr );
940 return aTransGraphic;
943 Graphic GraphicObject::GetTransformedGraphic( const GraphicAttr* pAttr ) const // TODO: Change to Impl
945 GetGraphic();
947 Graphic aGraphic;
948 GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
950 if( maGraphic.IsSupportedGraphic() && !maGraphic.IsSwapOut() )
952 if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() )
954 if( GetType() == GRAPHIC_BITMAP )
956 if( IsAnimated() )
958 Animation aAnimation( maGraphic.GetAnimation() );
959 GraphicManager::ImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL );
960 aAnimation.SetLoopCount( mnAnimationLoopCount );
961 aGraphic = aAnimation;
963 else
965 BitmapEx aBmpEx( maGraphic.GetBitmapEx() );
966 GraphicManager::ImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL );
967 aGraphic = aBmpEx;
970 else
972 GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() );
973 GraphicManager::ImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL );
974 aGraphic = aMtf;
977 else
979 if( ( GetType() == GRAPHIC_BITMAP ) && IsAnimated() )
981 Animation aAnimation( maGraphic.GetAnimation() );
982 aAnimation.SetLoopCount( mnAnimationLoopCount );
983 aGraphic = aAnimation;
985 else
986 aGraphic = maGraphic;
990 return aGraphic;
993 bool GraphicObject::SwapOut()
995 const bool bRet = !mbAutoSwapped && maGraphic.SwapOut();
997 if( bRet && mpMgr )
998 mpMgr->ImplGraphicObjectWasSwappedOut( *this );
1000 return bRet;
1003 bool GraphicObject::SwapOut( SvStream* pOStm )
1005 bool bRet = !mbAutoSwapped;
1006 // swap out as a link
1007 if( pOStm == GRFMGR_AUTOSWAPSTREAM_LINK )
1009 maGraphic.SwapOutAsLink();
1011 else
1013 bRet = bRet && maGraphic.SwapOut( pOStm );
1016 if( bRet && mpMgr )
1017 mpMgr->ImplGraphicObjectWasSwappedOut( *this );
1019 return bRet;
1022 bool GraphicObject::SwapIn()
1024 bool bRet = false;
1026 if( mbAutoSwapped )
1028 ImplAutoSwapIn();
1029 bRet = true;
1031 else
1033 bRet = maGraphic.SwapIn();
1035 if( bRet && mpMgr )
1036 mpMgr->ImplGraphicObjectWasSwappedIn( *this );
1039 if( bRet )
1041 ImplAssignGraphicData();
1044 return bRet;
1047 void GraphicObject::SetSwapState()
1049 if( !IsSwappedOut() )
1051 mbAutoSwapped = true;
1053 if( mpMgr )
1054 mpMgr->ImplGraphicObjectWasSwappedOut( *this );
1058 IMPL_LINK_NOARG_TYPED(GraphicObject, ImplAutoSwapOutHdl, Timer *, void)
1060 if( !IsSwappedOut() )
1062 mbIsInSwapOut = true;
1064 SvStream* pStream = GetSwapStream();
1066 if( GRFMGR_AUTOSWAPSTREAM_NONE != pStream )
1068 if( GRFMGR_AUTOSWAPSTREAM_LINK == pStream )
1069 mbAutoSwapped = SwapOut( GRFMGR_AUTOSWAPSTREAM_LINK );
1070 else
1072 if( GRFMGR_AUTOSWAPSTREAM_TEMP == pStream )
1073 mbAutoSwapped = SwapOut();
1074 else
1076 mbAutoSwapped = SwapOut( pStream );
1077 delete pStream;
1082 mbIsInSwapOut = false;
1085 if( mpSwapOutTimer )
1086 mpSwapOutTimer->Start();
1089 SvStream& ReadGraphicObject( SvStream& rIStm, GraphicObject& rGraphicObj )
1091 VersionCompat aCompat( rIStm, StreamMode::READ );
1092 Graphic aGraphic;
1093 GraphicAttr aAttr;
1094 bool bLink;
1096 ReadGraphic( rIStm, aGraphic );
1097 ReadGraphicAttr( rIStm, aAttr );
1098 rIStm.ReadCharAsBool( bLink );
1100 rGraphicObj.SetGraphic( aGraphic );
1101 rGraphicObj.SetAttr( aAttr );
1103 if( bLink )
1105 OUString aLink = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm, RTL_TEXTENCODING_UTF8);
1106 rGraphicObj.SetLink(aLink);
1108 else
1109 rGraphicObj.SetLink();
1111 rGraphicObj.SetSwapStreamHdl();
1113 return rIStm;
1116 SvStream& WriteGraphicObject( SvStream& rOStm, const GraphicObject& rGraphicObj )
1118 VersionCompat aCompat( rOStm, StreamMode::WRITE, 1 );
1119 const bool bLink = rGraphicObj.HasLink();
1121 WriteGraphic( rOStm, rGraphicObj.GetGraphic() );
1122 WriteGraphicAttr( rOStm, rGraphicObj.GetAttr() );
1123 rOStm.WriteBool( bLink );
1125 if( bLink )
1126 write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, rGraphicObj.GetLink(), RTL_TEXTENCODING_UTF8);
1128 return rOStm;
1131 #define UNO_NAME_GRAPHOBJ_URLPREFIX "vnd.sun.star.GraphicObject:"
1133 GraphicObject GraphicObject::CreateGraphicObjectFromURL( const OUString &rURL )
1135 const OUString aURL( rURL ), aPrefix( UNO_NAME_GRAPHOBJ_URLPREFIX );
1136 if( aURL.startsWith( aPrefix ) )
1138 // graphic manager url
1139 OString aUniqueID(OUStringToOString(rURL.copy(sizeof(UNO_NAME_GRAPHOBJ_URLPREFIX) - 1), RTL_TEXTENCODING_UTF8));
1140 return GraphicObject( aUniqueID );
1142 else
1144 Graphic aGraphic;
1145 if ( !aURL.isEmpty() )
1147 boost::scoped_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( aURL, StreamMode::READ ));
1148 if( pStream )
1149 GraphicConverter::Import( *pStream, aGraphic );
1152 return GraphicObject( aGraphic );
1156 void
1157 GraphicObject::InspectForGraphicObjectImageURL( const Reference< XInterface >& xIf, std::vector< OUString >& rvEmbedImgUrls )
1159 static const char sImageURL[] = "ImageURL";
1160 Reference< XPropertySet > xProps( xIf, UNO_QUERY );
1161 if ( xProps.is() )
1164 if ( xProps->getPropertySetInfo()->hasPropertyByName( sImageURL ) )
1166 OUString sURL;
1167 xProps->getPropertyValue( sImageURL ) >>= sURL;
1168 if ( !sURL.isEmpty() && sURL.startsWith( UNO_NAME_GRAPHOBJ_URLPREFIX ) )
1169 rvEmbedImgUrls.push_back( sURL );
1172 Reference< XNameContainer > xContainer( xIf, UNO_QUERY );
1173 if ( xContainer.is() )
1175 Sequence< OUString > sNames = xContainer->getElementNames();
1176 sal_Int32 nContainees = sNames.getLength();
1177 for ( sal_Int32 index = 0; index < nContainees; ++index )
1179 Reference< XInterface > xCtrl;
1180 xContainer->getByName( sNames[ index ] ) >>= xCtrl;
1181 InspectForGraphicObjectImageURL( xCtrl, rvEmbedImgUrls );
1186 // calculate scalings between real image size and logic object size. This
1187 // is necessary since the crop values are relative to original bitmap size
1188 basegfx::B2DVector GraphicObject::calculateCropScaling(
1189 double fWidth,
1190 double fHeight,
1191 double fLeftCrop,
1192 double fTopCrop,
1193 double fRightCrop,
1194 double fBottomCrop) const
1196 const MapMode aMapMode100thmm(MAP_100TH_MM);
1197 Size aBitmapSize(GetPrefSize());
1198 double fFactorX(1.0);
1199 double fFactorY(1.0);
1201 if(MAP_PIXEL == GetPrefMapMode().GetMapUnit())
1203 aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
1205 else
1207 aBitmapSize = OutputDevice::LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm);
1210 const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop);
1211 const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop);
1213 if(!basegfx::fTools::equalZero(fDivX))
1215 fFactorX = fabs(fWidth) / fDivX;
1218 if(!basegfx::fTools::equalZero(fDivY))
1220 fFactorY = fabs(fHeight) / fDivY;
1223 return basegfx::B2DVector(fFactorX,fFactorY);
1226 // restart SwapOut timer
1227 void GraphicObject::restartSwapOutTimer() const
1229 if( mpSwapOutTimer && mpSwapOutTimer->IsActive() )
1231 mpSwapOutTimer->Stop();
1232 mpSwapOutTimer->Start();
1236 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */