1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
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
72 mpMgr
->ImplCheckSizeOfSwappedInGraphics(this);
75 GraphicObject::GraphicObject( const GraphicManager
* pMgr
) :
80 ImplAssignGraphicData();
81 ImplSetGraphicManager( pMgr
);
84 GraphicObject::GraphicObject( const Graphic
& rGraphic
, const GraphicManager
* pMgr
) :
85 maGraphic ( rGraphic
),
90 ImplAssignGraphicData();
91 ImplSetGraphicManager( pMgr
);
94 GraphicObject::GraphicObject( const GraphicObject
& rGraphicObj
, const GraphicManager
* pMgr
) :
96 maGraphic ( rGraphicObj
.GetGraphic() ),
97 maAttr ( rGraphicObj
.maAttr
),
98 maLink ( rGraphicObj
.maLink
),
99 maUserData ( rGraphicObj
.maUserData
)
102 ImplAssignGraphicData();
103 ImplSetGraphicManager( pMgr
, NULL
, &rGraphicObj
);
104 if( rGraphicObj
.HasUserData() && rGraphicObj
.IsSwappedOut() )
108 GraphicObject::GraphicObject( const OString
& rUniqueID
, const GraphicManager
* pMgr
) :
114 // assign default properties
115 ImplAssignGraphicData();
117 ImplSetGraphicManager( pMgr
, &rUniqueID
);
120 ImplAssignGraphicData();
123 GraphicObject::~GraphicObject()
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()
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
) )
179 mpMgr
->ImplUnregisterObj( *this );
181 if( ( mpMgr
== mpGlobalMgr
) && !mpGlobalMgr
->ImplHasObjects() )
182 delete mpGlobalMgr
, mpGlobalMgr
= NULL
;
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());
202 mpMgr
= const_cast<GraphicManager
*>(pMgr
);
204 mpMgr
->ImplRegisterObj( *this, maGraphic
, pID
, pCopyObj
);
209 void GraphicObject::ImplAutoSwapIn()
216 if( maGraphic
.SwapIn() )
217 mbAutoSwapped
= false;
220 SvStream
* pStream
= GetSwapStream();
222 if( GRFMGR_AUTOSWAPSTREAM_NONE
!= pStream
)
224 if( GRFMGR_AUTOSWAPSTREAM_LINK
== pStream
)
230 if( ::utl::LocalFileHelper::ConvertPhysicalNameToURL( GetLink(), aURLStr
) )
232 boost::scoped_ptr
<SvStream
> pIStm(::utl::UcbStreamHelper::CreateStream( aURLStr
, StreamMode::READ
));
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();
248 mbAutoSwapped
= !maGraphic
.SwapIn( pStream
);
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
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
);
280 long nTotalWidth
, nTotalHeight
;
284 aClipPoly
.Rotate( rPt
, nRot10
);
285 bRectClipRegion
= false;
288 bRectClipRegion
= true;
290 rClipPolyPoly
= aClipPoly
;
292 if( maGraphic
.GetPrefMapMode() == MAP_PIXEL
)
293 aSize100
= Application::GetDefaultDevice()->PixelToLogic( maGraphic
.GetPrefSize(), aMap100
);
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
);
323 Polygon
aOriginPoly( 1 );
325 aOriginPoly
[ 0 ] = rPt
;
326 aOriginPoly
.Rotate( aOldOrigin
, nRot10
);
327 rPt
= aOriginPoly
[ 0 ];
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() )
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();
391 aRet
= mpMgr
->ImplGetUniqueID( *this );
396 SvStream
* GraphicObject::GetSwapStream() const
398 if( HasSwapStreamHdl() )
399 return reinterpret_cast<SvStream
*>( mpSwapStreamHdl
->Call( const_cast<void*>(static_cast<const void*>(this)) ) );
401 return GRFMGR_AUTOSWAPSTREAM_NONE
;
404 void GraphicObject::SetAttr( const GraphicAttr
& rAttr
)
408 if( mpSimpleCache
&& ( mpSimpleCache
->maAttr
!= rAttr
) )
409 delete mpSimpleCache
, mpSimpleCache
= NULL
;
412 void GraphicObject::SetLink()
417 void GraphicObject::SetLink( const OUString
& rLink
)
422 void GraphicObject::SetUserData()
427 void GraphicObject::SetUserData( const OUString
& rUserData
)
429 maUserData
= rUserData
;
430 if( !rUserData
.isEmpty() )
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();
469 delete mpSwapOutTimer
, mpSwapOutTimer
= NULL
;
472 void GraphicObject::FireSwapInRequest()
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
486 ImplSetGraphicManager( NULL
);
489 bool GraphicObject::IsCached( OutputDevice
* pOut
, const Point
& rPt
, const Size
& rSz
,
490 const GraphicAttr
* pAttr
, GraphicManagerDrawFlags nFlags
) const
494 if( nFlags
& GraphicManagerDrawFlags::CACHED
)
498 if ( pAttr
&& pAttr
->IsCropped() )
500 tools::PolyPolygon aClipPolyPoly
;
502 ImplGetCropParams( pOut
, aPt
, aSz
, pAttr
, aClipPolyPoly
, bRectClip
);
504 bRet
= mpMgr
->IsInCache( pOut
, aPt
, aSz
, *this, ( pAttr
? *pAttr
: GetAttr() ) );
512 bool GraphicObject::Draw( OutputDevice
* pOut
, const Point
& rPt
, const Size
& rSz
,
513 const GraphicAttr
* pAttr
, GraphicManagerDrawFlags nFlags
)
515 GraphicAttr
aAttr( pAttr
? *pAttr
: GetAttr() );
518 const DrawModeFlags nOldDrawMode
= pOut
->GetDrawMode();
519 bool bCropped
= aAttr
.IsCropped();
520 bool bCached
= false;
523 // #i29534# Provide output rects for PDF writer
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
);
547 tools::PolyPolygon aClipPolyPoly
;
549 const bool bCrop
= ImplGetCropParams( pOut
, aPt
, aSz
, &aAttr
, aClipPolyPoly
, bRectClip
);
551 pOut
->Push( PushFlags::CLIPREGION
);
557 // #i29534# Store crop rect for later forwarding to
559 aCropRect
= aClipPolyPoly
.GetBoundRect();
560 pOut
->IntersectClipRegion( aCropRect
);
564 pOut
->IntersectClipRegion(vcl::Region(aClipPolyPoly
));
569 bRet
= mpMgr
->DrawObj( pOut
, aPt
, aSz
, *this, aAttr
, nFlags
, bCached
);
574 pOut
->SetDrawMode( nOldDrawMode
);
576 // #i29534# Moved below OutDev restoration, to avoid multiple swap-ins
577 // (code above needs to call GetGraphic twice)
581 mpSwapOutTimer
->Start();
583 FireSwapOutRequest();
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 )
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
)
619 if( !IsSwappedOut() )
621 const GraphicAttr
aAttr( pAttr
? *pAttr
: GetAttr() );
627 bool bCropped
= aAttr
.IsCropped();
631 tools::PolyPolygon aClipPolyPoly
;
633 const bool bCrop
= ImplGetCropParams( pOut
, aPt
, aSz
, &aAttr
, aClipPolyPoly
, bRectClip
);
635 pOut
->Push( PushFlags::CLIPREGION
);
640 pOut
->IntersectClipRegion( aClipPolyPoly
.GetBoundRect() );
642 pOut
->IntersectClipRegion(vcl::Region(aClipPolyPoly
));
646 if( !mpSimpleCache
|| ( mpSimpleCache
->maAttr
!= aAttr
) || pFirstFrameOutDev
)
649 delete mpSimpleCache
;
651 mpSimpleCache
= new GrfSimpleCacheObj( GetTransformedGraphic( &aAttr
), aAttr
);
652 mpSimpleCache
->maGraphic
.SetAnimationNotifyHdl( GetAnimationNotifyHdl() );
655 mpSimpleCache
->maGraphic
.StartAnimation( pOut
, aPt
, aSz
, nExtraData
, pFirstFrameOutDev
);
663 bRet
= Draw( pOut
, rPt
, rSz
, &aAttr
, GraphicManagerDrawFlags::STANDARD
);
669 void GraphicObject::StopAnimation( OutputDevice
* pOut
, long nExtraData
)
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
683 pThis
->restartSwapOutTimer();
688 void GraphicObject::SetGraphic( const Graphic
& rGraphic
, const GraphicObject
* pCopyObj
)
690 mpMgr
->ImplUnregisterObj( *this );
693 mpSwapOutTimer
->Stop();
695 maGraphic
= rGraphic
;
696 mbAutoSwapped
= false;
697 ImplAssignGraphicData();
699 delete mpSimpleCache
, mpSimpleCache
= NULL
;
701 mpMgr
->ImplRegisterObj( *this, maGraphic
, 0, pCopyObj
);
704 mpSwapOutTimer
->Start();
709 void GraphicObject::SetGraphic( const Graphic
& rGraphic
, const OUString
& rLink
)
711 SetGraphic( rGraphic
);
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
);
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()),
739 aCropRightBottom
= Application::GetDefaultDevice()->LogicToPixel(
740 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
745 // crops are in GraphicObject units -> to aMapGraph
746 aCropLeftTop
= OutputDevice::LogicToLogic(
747 Size(rAttr
.GetLeftCrop(), rAttr
.GetTopCrop()),
750 aCropRightBottom
= OutputDevice::LogicToLogic(
751 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
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
);
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() );
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()),
812 aCropRightBottom
= Application::GetDefaultDevice()->LogicToPixel(
813 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
818 // crops are in GraphicObject units -> to MAP_PIXEL
819 aCropLeftTop
= Application::GetDefaultDevice()->LogicToPixel(
820 Size(rAttr
.GetLeftCrop(), rAttr
.GetTopCrop()),
822 aCropRightBottom
= Application::GetDefaultDevice()->LogicToPixel(
823 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
827 // convert from prefmapmode to pixel
829 Application::GetDefaultDevice()->LogicToPixel(
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() )
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
;
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
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
)
958 Animation
aAnimation( maGraphic
.GetAnimation() );
959 GraphicManager::ImplAdjust( aAnimation
, aAttr
, GraphicAdjustmentFlags::ALL
);
960 aAnimation
.SetLoopCount( mnAnimationLoopCount
);
961 aGraphic
= aAnimation
;
965 BitmapEx
aBmpEx( maGraphic
.GetBitmapEx() );
966 GraphicManager::ImplAdjust( aBmpEx
, aAttr
, GraphicAdjustmentFlags::ALL
);
972 GDIMetaFile
aMtf( maGraphic
.GetGDIMetaFile() );
973 GraphicManager::ImplAdjust( aMtf
, aAttr
, GraphicAdjustmentFlags::ALL
);
979 if( ( GetType() == GRAPHIC_BITMAP
) && IsAnimated() )
981 Animation
aAnimation( maGraphic
.GetAnimation() );
982 aAnimation
.SetLoopCount( mnAnimationLoopCount
);
983 aGraphic
= aAnimation
;
986 aGraphic
= maGraphic
;
993 bool GraphicObject::SwapOut()
995 const bool bRet
= !mbAutoSwapped
&& maGraphic
.SwapOut();
998 mpMgr
->ImplGraphicObjectWasSwappedOut( *this );
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();
1013 bRet
= bRet
&& maGraphic
.SwapOut( pOStm
);
1017 mpMgr
->ImplGraphicObjectWasSwappedOut( *this );
1022 bool GraphicObject::SwapIn()
1033 bRet
= maGraphic
.SwapIn();
1036 mpMgr
->ImplGraphicObjectWasSwappedIn( *this );
1041 ImplAssignGraphicData();
1047 void GraphicObject::SetSwapState()
1049 if( !IsSwappedOut() )
1051 mbAutoSwapped
= true;
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
);
1072 if( GRFMGR_AUTOSWAPSTREAM_TEMP
== pStream
)
1073 mbAutoSwapped
= SwapOut();
1076 mbAutoSwapped
= SwapOut( pStream
);
1082 mbIsInSwapOut
= false;
1085 if( mpSwapOutTimer
)
1086 mpSwapOutTimer
->Start();
1089 SvStream
& ReadGraphicObject( SvStream
& rIStm
, GraphicObject
& rGraphicObj
)
1091 VersionCompat
aCompat( rIStm
, StreamMode::READ
);
1096 ReadGraphic( rIStm
, aGraphic
);
1097 ReadGraphicAttr( rIStm
, aAttr
);
1098 rIStm
.ReadCharAsBool( bLink
);
1100 rGraphicObj
.SetGraphic( aGraphic
);
1101 rGraphicObj
.SetAttr( aAttr
);
1105 OUString aLink
= read_uInt16_lenPrefixed_uInt8s_ToOUString(rIStm
, RTL_TEXTENCODING_UTF8
);
1106 rGraphicObj
.SetLink(aLink
);
1109 rGraphicObj
.SetLink();
1111 rGraphicObj
.SetSwapStreamHdl();
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
);
1126 write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm
, rGraphicObj
.GetLink(), RTL_TEXTENCODING_UTF8
);
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
);
1145 if ( !aURL
.isEmpty() )
1147 boost::scoped_ptr
<SvStream
> pStream(utl::UcbStreamHelper::CreateStream( aURL
, StreamMode::READ
));
1149 GraphicConverter::Import( *pStream
, aGraphic
);
1152 return GraphicObject( aGraphic
);
1157 GraphicObject::InspectForGraphicObjectImageURL( const Reference
< XInterface
>& xIf
, std::vector
< OUString
>& rvEmbedImgUrls
)
1159 static const char sImageURL
[] = "ImageURL";
1160 Reference
< XPropertySet
> xProps( xIf
, UNO_QUERY
);
1164 if ( xProps
->getPropertySetInfo()->hasPropertyByName( sImageURL
) )
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(
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
);
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: */