xref: /trunk/main/svtools/source/graphic/grfcache.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svtools.hxx"
30 
31 #include <vos/timer.hxx>
32 #include <tools/debug.hxx>
33 #include <vcl/outdev.hxx>
34 #include <tools/poly.hxx>
35 #include "grfcache.hxx"
36 
37 #include <memory>
38 
39 // -----------
40 // - Defines -
41 // -----------
42 
43 #define RELEASE_TIMEOUT 10000
44 #define MAX_BMP_EXTENT	4096
45 
46 // -----------
47 // - statics -
48 // -----------
49 
50 static const char aHexData[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
51 
52 // -------------
53 // - GraphicID -
54 // -------------
55 
56 class GraphicID
57 {
58 private:
59 
60 	sal_uInt32	mnID1;
61 	sal_uInt32	mnID2;
62 	sal_uInt32	mnID3;
63 	sal_uInt32	mnID4;
64 
65 				GraphicID();
66 
67 public:
68 
69 
70 				GraphicID( const GraphicObject& rObj );
71 				~GraphicID() {}
72 
73 	sal_Bool		operator==( const GraphicID& rID ) const
74 				{
75 					return( rID.mnID1 == mnID1 && rID.mnID2 == mnID2 &&
76 							rID.mnID3 == mnID3 && rID.mnID4 == mnID4 );
77 				}
78 
79 	ByteString	GetIDString() const;
80     sal_Bool        IsEmpty() const { return( 0 == mnID4 ); }
81 };
82 
83 // -----------------------------------------------------------------------------
84 
85 GraphicID::GraphicID( const GraphicObject& rObj )
86 {
87 	const Graphic& rGraphic = rObj.GetGraphic();
88 
89 	mnID1 = ( (sal_uLong) rGraphic.GetType() ) << 28;
90 
91 	switch( rGraphic.GetType() )
92 	{
93 		case( GRAPHIC_BITMAP ):
94 		{
95 			if( rGraphic.IsAnimated() )
96 			{
97 				const Animation aAnimation( rGraphic.GetAnimation() );
98 
99 				mnID1 |= ( aAnimation.Count() & 0x0fffffff );
100 				mnID2 = aAnimation.GetDisplaySizePixel().Width();
101 				mnID3 = aAnimation.GetDisplaySizePixel().Height();
102 				mnID4 = rGraphic.GetChecksum();
103 			}
104 			else
105 			{
106 				const BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
107 
108 				mnID1 |= ( ( ( (sal_uLong) aBmpEx.GetTransparentType() << 8 ) | ( aBmpEx.IsAlpha() ? 1 : 0 ) ) & 0x0fffffff );
109 				mnID2 = aBmpEx.GetSizePixel().Width();
110 				mnID3 = aBmpEx.GetSizePixel().Height();
111 				mnID4 = rGraphic.GetChecksum();
112 			}
113 		}
114 		break;
115 
116 		case( GRAPHIC_GDIMETAFILE ):
117 		{
118 			const GDIMetaFile aMtf( rGraphic.GetGDIMetaFile() );
119 
120 			mnID1 |= ( aMtf.GetActionCount() & 0x0fffffff );
121 			mnID2 = aMtf.GetPrefSize().Width();
122 			mnID3 = aMtf.GetPrefSize().Height();
123 			mnID4 = rGraphic.GetChecksum();
124 		}
125 		break;
126 
127 		default:
128 			mnID2 = mnID3 = mnID4 = 0;
129 		break;
130 	}
131 }
132 
133 // -----------------------------------------------------------------------------
134 
135 ByteString GraphicID::GetIDString() const
136 {
137 	ByteString	aHexStr;
138 	sal_Char*	pStr = aHexStr.AllocBuffer( 32 );
139 	sal_Int32	nShift;
140 
141 	for( nShift = 28; nShift >= 0; nShift -= 4 )
142 		*pStr++ = aHexData[ ( mnID1 >> (sal_uInt32) nShift ) & 0xf ];
143 
144 	for( nShift = 28; nShift >= 0; nShift -= 4 )
145 		*pStr++ = aHexData[ ( mnID2 >> (sal_uInt32) nShift ) & 0xf ];
146 
147 	for( nShift = 28; nShift >= 0; nShift -= 4 )
148 		*pStr++ = aHexData[ ( mnID3 >> (sal_uInt32) nShift ) & 0xf ];
149 
150 	for( nShift = 28; nShift >= 0; nShift -= 4 )
151 		*pStr++ = aHexData[ ( mnID4 >> (sal_uInt32) nShift ) & 0xf ];
152 
153 	return aHexStr;
154 }
155 
156 // ---------------------
157 // - GraphicCacheEntry -
158 // ---------------------
159 
160 class GraphicCacheEntry
161 {
162 private:
163 
164 	List				maGraphicObjectList;
165 	GraphicID			maID;
166 	GfxLink				maGfxLink;
167 	BitmapEx*			mpBmpEx;
168 	GDIMetaFile*		mpMtf;
169 	Animation*			mpAnimation;
170 	sal_Bool				mbSwappedAll;
171 
172 	sal_Bool				ImplInit( const GraphicObject& rObj );
173 	sal_Bool				ImplMatches( const GraphicObject& rObj ) const { return( GraphicID( rObj ) == maID ); }
174 	void				ImplFillSubstitute( Graphic& rSubstitute );
175 
176 public:
177 
178 						GraphicCacheEntry( const GraphicObject& rObj );
179 						~GraphicCacheEntry();
180 
181 	const GraphicID&	GetID() const { return maID; }
182 
183 	void				AddGraphicObjectReference( const GraphicObject& rObj, Graphic& rSubstitute );
184 	sal_Bool				ReleaseGraphicObjectReference( const GraphicObject& rObj );
185 	sal_uLong				GetGraphicObjectReferenceCount() { return maGraphicObjectList.Count(); }
186 	sal_Bool				HasGraphicObjectReference( const GraphicObject& rObj );
187 
188 	void				TryToSwapIn();
189 	void				GraphicObjectWasSwappedOut( const GraphicObject& rObj );
190 	sal_Bool				FillSwappedGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute );
191 	void				GraphicObjectWasSwappedIn( const GraphicObject& rObj );
192 };
193 
194 // -----------------------------------------------------------------------------
195 
196 GraphicCacheEntry::GraphicCacheEntry( const GraphicObject& rObj ) :
197 	maID			( rObj ),
198 	mpBmpEx			( NULL ),
199 	mpMtf			( NULL ),
200 	mpAnimation		( NULL ),
201 	mbSwappedAll    ( !ImplInit( rObj ) )
202 {
203 	maGraphicObjectList.Insert( (void*) &rObj, LIST_APPEND );
204 }
205 
206 // -----------------------------------------------------------------------------
207 
208 GraphicCacheEntry::~GraphicCacheEntry()
209 {
210 	DBG_ASSERT( !maGraphicObjectList.Count(), "GraphicCacheEntry::~GraphicCacheEntry(): Not all GraphicObjects are removed from this entry" );
211 
212 	delete mpBmpEx;
213 	delete mpMtf;
214 	delete mpAnimation;
215 }
216 
217 // -----------------------------------------------------------------------------
218 
219 sal_Bool GraphicCacheEntry::ImplInit( const GraphicObject& rObj )
220 {
221 	sal_Bool bRet;
222 
223 	if( !rObj.IsSwappedOut() )
224 	{
225 		const Graphic& rGraphic = rObj.GetGraphic();
226 
227 		if( mpBmpEx )
228 			delete mpBmpEx, mpBmpEx = NULL;
229 
230 		if( mpMtf )
231 			delete mpMtf, mpMtf = NULL;
232 
233 		if( mpAnimation )
234 			delete mpAnimation, mpAnimation = NULL;
235 
236 		switch( rGraphic.GetType() )
237 		{
238 			case( GRAPHIC_BITMAP ):
239 			{
240 				if( rGraphic.IsAnimated() )
241 					mpAnimation = new Animation( rGraphic.GetAnimation() );
242 				else
243 					mpBmpEx = new BitmapEx( rGraphic.GetBitmapEx() );
244 			}
245 			break;
246 
247 			case( GRAPHIC_GDIMETAFILE ):
248 			{
249 				mpMtf = new GDIMetaFile( rGraphic.GetGDIMetaFile() );
250 			}
251 			break;
252 
253 			default:
254 				DBG_ASSERT( GetID().IsEmpty(), "GraphicCacheEntry::ImplInit: Could not initialize graphic! (=>KA)" );
255 			break;
256 		}
257 
258 		if( rGraphic.IsLink() )
259 			maGfxLink = ( (Graphic&) rGraphic ).GetLink();
260 		else
261 			maGfxLink = GfxLink();
262 
263 		bRet = sal_True;
264 	}
265 	else
266 		bRet = sal_False;
267 
268 	return bRet;
269 }
270 
271 // -----------------------------------------------------------------------------
272 
273 void GraphicCacheEntry::ImplFillSubstitute( Graphic& rSubstitute )
274 {
275 	// create substitute for graphic;
276 	const Size			aPrefSize( rSubstitute.GetPrefSize() );
277 	const MapMode		aPrefMapMode( rSubstitute.GetPrefMapMode() );
278 	const Link			aAnimationNotifyHdl( rSubstitute.GetAnimationNotifyHdl() );
279 	const String		aDocFileName( rSubstitute.GetDocFileName() );
280 	const sal_uLong			nDocFilePos = rSubstitute.GetDocFilePos();
281 	const GraphicType	eOldType = rSubstitute.GetType();
282 	const sal_Bool			bDefaultType = ( rSubstitute.GetType() == GRAPHIC_DEFAULT );
283 
284 	if( rSubstitute.IsLink() && ( GFX_LINK_TYPE_NONE == maGfxLink.GetType() ) )
285 		maGfxLink = rSubstitute.GetLink();
286 
287 	if( mpBmpEx )
288 		rSubstitute = *mpBmpEx;
289 	else if( mpAnimation )
290 		rSubstitute = *mpAnimation;
291 	else if( mpMtf )
292 		rSubstitute = *mpMtf;
293 	else
294 		rSubstitute.Clear();
295 
296 	if( eOldType != GRAPHIC_NONE )
297 	{
298 		rSubstitute.SetPrefSize( aPrefSize );
299 		rSubstitute.SetPrefMapMode( aPrefMapMode );
300 		rSubstitute.SetAnimationNotifyHdl( aAnimationNotifyHdl );
301 		rSubstitute.SetDocFileName( aDocFileName, nDocFilePos );
302 	}
303 
304 	if( GFX_LINK_TYPE_NONE != maGfxLink.GetType() )
305 		rSubstitute.SetLink( maGfxLink );
306 
307 	if( bDefaultType )
308 		rSubstitute.SetDefaultType();
309 }
310 
311 // -----------------------------------------------------------------------------
312 
313 void GraphicCacheEntry::AddGraphicObjectReference( const GraphicObject& rObj, Graphic& rSubstitute )
314 {
315 	if( mbSwappedAll )
316 		mbSwappedAll = !ImplInit( rObj );
317 
318 	ImplFillSubstitute( rSubstitute );
319 	maGraphicObjectList.Insert( (void*) &rObj, LIST_APPEND );
320 }
321 
322 // -----------------------------------------------------------------------------
323 
324 sal_Bool GraphicCacheEntry::ReleaseGraphicObjectReference( const GraphicObject& rObj )
325 {
326 	sal_Bool bRet = sal_False;
327 
328 	for( void* pObj = maGraphicObjectList.First(); !bRet && pObj; pObj = maGraphicObjectList.Next() )
329 	{
330 		if( &rObj == (GraphicObject*) pObj )
331 		{
332 			maGraphicObjectList.Remove( pObj );
333 			bRet = sal_True;
334 		}
335 	}
336 
337 	return bRet;
338 }
339 
340 // -----------------------------------------------------------------------------
341 
342 sal_Bool GraphicCacheEntry::HasGraphicObjectReference( const GraphicObject& rObj )
343 {
344 	sal_Bool bRet = sal_False;
345 
346 	for( void* pObj = maGraphicObjectList.First(); !bRet && pObj; pObj = maGraphicObjectList.Next() )
347 		if( &rObj == (GraphicObject*) pObj )
348 			bRet = sal_True;
349 
350 	return bRet;
351 }
352 
353 // -----------------------------------------------------------------------------
354 
355 void GraphicCacheEntry::TryToSwapIn()
356 {
357 	if( mbSwappedAll && maGraphicObjectList.Count() )
358 		( (GraphicObject*) maGraphicObjectList.First() )->FireSwapInRequest();
359 }
360 
361 // -----------------------------------------------------------------------------
362 
363 void GraphicCacheEntry::GraphicObjectWasSwappedOut( const GraphicObject& /*rObj*/ )
364 {
365 	mbSwappedAll = sal_True;
366 
367 	for( void* pObj = maGraphicObjectList.First(); mbSwappedAll && pObj; pObj = maGraphicObjectList.Next() )
368 		if( !( (GraphicObject*) pObj )->IsSwappedOut() )
369 			mbSwappedAll = sal_False;
370 
371 	if( mbSwappedAll )
372 	{
373 		delete mpBmpEx, mpBmpEx = NULL;
374 		delete mpMtf, mpMtf = NULL;
375 		delete mpAnimation, mpAnimation = NULL;
376 	}
377 }
378 
379 // -----------------------------------------------------------------------------
380 
381 sal_Bool GraphicCacheEntry::FillSwappedGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute )
382 {
383 	sal_Bool bRet;
384 
385 	if( !mbSwappedAll && rObj.IsSwappedOut() )
386 	{
387 		ImplFillSubstitute( rSubstitute );
388 		bRet = sal_True;
389 	}
390 	else
391 		bRet = sal_False;
392 
393 	return bRet;
394 }
395 
396 // -----------------------------------------------------------------------------
397 
398 void GraphicCacheEntry::GraphicObjectWasSwappedIn( const GraphicObject& rObj )
399 {
400 	if( mbSwappedAll )
401 		mbSwappedAll = !ImplInit( rObj );
402 }
403 
404 // ----------------------------
405 // - GraphicDisplayCacheEntry -
406 // ----------------------------
407 
408 class GraphicDisplayCacheEntry
409 {
410 private:
411 
412 	::vos::TTimeValue           maReleaseTime;
413     const GraphicCacheEntry*	mpRefCacheEntry;
414 	GDIMetaFile*				mpMtf;
415 	BitmapEx*					mpBmpEx;
416 	GraphicAttr					maAttr;
417 	Size						maOutSizePix;
418 	sal_uLong						mnCacheSize;
419     sal_uLong						mnOutDevDrawMode;
420     sal_uInt16						mnOutDevBitCount;
421 
422 public:
423 
424 	static sal_uLong				GetNeededSize( OutputDevice* pOut, const Point& rPt, const Size& rSz,
425 											   const GraphicObject& rObj, const GraphicAttr& rAttr );
426 
427 public:
428 
429 								GraphicDisplayCacheEntry( const GraphicCacheEntry* pRefCacheEntry,
430 														  OutputDevice* pOut, const Point& rPt, const Size& rSz,
431 														  const GraphicObject& rObj, const GraphicAttr& rAttr,
432 														  const BitmapEx& rBmpEx ) :
433 									mpRefCacheEntry( pRefCacheEntry ),
434 									mpMtf( NULL ), mpBmpEx( new BitmapEx( rBmpEx ) ),
435 									maAttr( rAttr ), maOutSizePix( pOut->LogicToPixel( rSz ) ),
436 									mnCacheSize( GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) ),
437                                     mnOutDevDrawMode( pOut->GetDrawMode() ),
438                                     mnOutDevBitCount( pOut->GetBitCount() )
439                                     {
440                                     }
441 
442 								GraphicDisplayCacheEntry( const GraphicCacheEntry* pRefCacheEntry,
443 														  OutputDevice* pOut, const Point& rPt, const Size& rSz,
444 														  const GraphicObject& rObj, const GraphicAttr& rAttr,
445 														  const GDIMetaFile& rMtf ) :
446 									mpRefCacheEntry( pRefCacheEntry ),
447 									mpMtf( new GDIMetaFile( rMtf ) ), mpBmpEx( NULL ),
448 									maAttr( rAttr ), maOutSizePix( pOut->LogicToPixel( rSz ) ),
449 									mnCacheSize( GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) ),
450                                     mnOutDevDrawMode( pOut->GetDrawMode() ),
451                                     mnOutDevBitCount( pOut->GetBitCount() )
452                                     {
453                                     }
454 
455 
456 								~GraphicDisplayCacheEntry();
457 
458 	const GraphicAttr&			GetAttr() const { return maAttr; }
459 	const Size&					GetOutputSizePixel() const { return maOutSizePix; }
460 	sal_uLong					GetCacheSize() const { return mnCacheSize; }
461 	const GraphicCacheEntry*	GetReferencedCacheEntry() const { return mpRefCacheEntry; }
462     sal_uLong					GetOutDevDrawMode() const { return mnOutDevDrawMode; }
463     sal_uInt16				GetOutDevBitCount()	const { return mnOutDevBitCount; }
464 
465     void                        SetReleaseTime( const ::vos::TTimeValue& rReleaseTime ) { maReleaseTime = rReleaseTime; }
466     const ::vos::TTimeValue&    GetReleaseTime() const { return maReleaseTime; }
467 
468 	sal_Bool						Matches( OutputDevice* pOut, const Point& /*rPtPixel*/, const Size& rSzPixel,
469 										 const GraphicCacheEntry* pCacheEntry, const GraphicAttr& rAttr ) const
470 								{
471                                     // #i46805# Additional match
472                                     // criteria: outdev draw mode and
473                                     // bit count. One cannot reuse
474                                     // this cache object, if it's
475                                     // e.g. generated for
476                                     // DRAWMODE_GRAYBITMAP.
477 									return( ( pCacheEntry == mpRefCacheEntry ) &&
478 											( maAttr == rAttr ) &&
479 											( ( maOutSizePix == rSzPixel ) || ( !maOutSizePix.Width() && !maOutSizePix.Height() ) ) &&
480                                             ( pOut->GetBitCount() == mnOutDevBitCount ) &&
481                                             ( pOut->GetDrawMode() == mnOutDevDrawMode ) );
482 								}
483 
484 	void						Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz ) const;
485 };
486 
487 // -----------------------------------------------------------------------------
488 
489 sal_uLong GraphicDisplayCacheEntry::GetNeededSize( OutputDevice* pOut, const Point& /*rPt*/, const Size& rSz,
490 											   const GraphicObject& rObj, const GraphicAttr& rAttr )
491 {
492     const Graphic&      rGraphic = rObj.GetGraphic();
493     const GraphicType	eType = rGraphic.GetType();
494 	sal_uLong				nNeededSize;
495 
496 	if( GRAPHIC_BITMAP == eType )
497 	{
498 		const Size aOutSizePix( pOut->LogicToPixel( rSz ) );
499 		const long nBitCount = pOut->GetBitCount();
500 
501 		if( ( aOutSizePix.Width() > MAX_BMP_EXTENT ) ||
502             ( aOutSizePix.Height() > MAX_BMP_EXTENT ) )
503         {
504 		    nNeededSize = ULONG_MAX;
505         }
506 		else if( nBitCount )
507 		{
508 		    nNeededSize = aOutSizePix.Width() * aOutSizePix.Height() * nBitCount / 8;
509 
510     		if( rObj.IsTransparent() || ( rAttr.GetRotation() % 3600 ) )
511 	    		nNeededSize += nNeededSize / nBitCount;
512 		}
513 		else
514 	    {
515      		DBG_ERROR( "GraphicDisplayCacheEntry::GetNeededSize(): pOut->GetBitCount() == 0" );
516 		    nNeededSize = 256000;
517         }
518 	}
519 	else if( GRAPHIC_GDIMETAFILE == eType )
520 		nNeededSize = rGraphic.GetSizeBytes();
521 	else
522 		nNeededSize = 0;
523 
524 	return nNeededSize;
525 }
526 
527 // -----------------------------------------------------------------------------
528 
529 GraphicDisplayCacheEntry::~GraphicDisplayCacheEntry()
530 {
531 	if( mpMtf )
532 		delete mpMtf;
533 
534 	if( mpBmpEx )
535 		delete mpBmpEx;
536 }
537 
538 // -----------------------------------------------------------------------------
539 
540 void GraphicDisplayCacheEntry::Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz ) const
541 {
542 	if( mpMtf )
543 		GraphicManager::ImplDraw( pOut, rPt, rSz, *mpMtf, maAttr );
544 	else if( mpBmpEx )
545 	{
546 		if( maAttr.IsRotated() )
547 		{
548 			Polygon aPoly( Rectangle( rPt, rSz ) );
549 
550 			aPoly.Rotate( rPt, maAttr.GetRotation() % 3600 );
551 			const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
552 			pOut->DrawBitmapEx( aRotBoundRect.TopLeft(), aRotBoundRect.GetSize(), *mpBmpEx );
553 		}
554 		else
555 			pOut->DrawBitmapEx( rPt, rSz, *mpBmpEx );
556 	}
557 }
558 
559 // -----------------------
560 // - GraphicCache -
561 // -----------------------
562 
563 GraphicCache::GraphicCache( GraphicManager& rMgr, sal_uLong nDisplayCacheSize, sal_uLong nMaxObjDisplayCacheSize ) :
564 	mrMgr				    ( rMgr ),
565     mnReleaseTimeoutSeconds ( 0UL ),
566 	mnMaxDisplaySize	    ( nDisplayCacheSize ),
567 	mnMaxObjDisplaySize	    ( nMaxObjDisplayCacheSize ),
568 	mnUsedDisplaySize	    ( 0UL )
569 {
570     maReleaseTimer.SetTimeoutHdl( LINK( this, GraphicCache, ReleaseTimeoutHdl ) );
571     maReleaseTimer.SetTimeout( RELEASE_TIMEOUT );
572     maReleaseTimer.Start();
573 }
574 
575 // -----------------------------------------------------------------------------
576 
577 GraphicCache::~GraphicCache()
578 {
579 	DBG_ASSERT( !maGraphicCache.Count(), "GraphicCache::~GraphicCache(): there are some GraphicObjects in cache" );
580 	DBG_ASSERT( !maDisplayCache.Count(), "GraphicCache::~GraphicCache(): there are some GraphicObjects in display cache" );
581 }
582 
583 // -----------------------------------------------------------------------------
584 
585 void GraphicCache::AddGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute,
586                                      const ByteString* pID, const GraphicObject* pCopyObj )
587 {
588 	sal_Bool bInserted = sal_False;
589 
590 	if( !rObj.IsSwappedOut() &&
591         ( pID || ( pCopyObj && ( pCopyObj->GetType() != GRAPHIC_NONE ) ) || ( rObj.GetType() != GRAPHIC_NONE ) ) )
592 	{
593         if( pCopyObj )
594         {
595             GraphicCacheEntry* pEntry = static_cast< GraphicCacheEntry* >( maGraphicCache.First() );
596 
597             while( !bInserted && pEntry )
598             {
599                 if( pEntry->HasGraphicObjectReference( *pCopyObj ) )
600                 {
601                     pEntry->AddGraphicObjectReference( rObj, rSubstitute );
602                     bInserted = sal_True;
603                 }
604                 else
605                 {
606                     pEntry = static_cast< GraphicCacheEntry* >( maGraphicCache.Next() );
607                 }
608             }
609         }
610 
611         if( !bInserted )
612         {
613             GraphicCacheEntry* pEntry = static_cast< GraphicCacheEntry* >( maGraphicCache.First() );
614             ::std::auto_ptr< GraphicID > apID;
615 
616             if( !pID )
617             {
618                 apID.reset( new GraphicID( rObj ) );
619             }
620 
621             while( !bInserted && pEntry )
622             {
623                 const GraphicID& rEntryID = pEntry->GetID();
624 
625                 if( pID )
626                 {
627                     if( rEntryID.GetIDString() == *pID )
628                     {
629                         pEntry->TryToSwapIn();
630 
631                         // since pEntry->TryToSwapIn can modify our current list, we have to
632                         // iterate from beginning to add a reference to the appropriate
633                         // CacheEntry object; after this, quickly jump out of the outer iteration
634                         for( pEntry = static_cast< GraphicCacheEntry* >( maGraphicCache.First() );
635                              !bInserted && pEntry;
636                              pEntry = static_cast< GraphicCacheEntry* >( maGraphicCache.Next() ) )
637                         {
638                             const GraphicID& rID = pEntry->GetID();
639 
640                             if( rID.GetIDString() == *pID )
641                             {
642                                 pEntry->AddGraphicObjectReference( rObj, rSubstitute );
643                                 bInserted = sal_True;
644                             }
645                         }
646 
647                         if( !bInserted )
648                         {
649                             maGraphicCache.Insert( new GraphicCacheEntry( rObj ), LIST_APPEND );
650                             bInserted = sal_True;
651                         }
652                     }
653                 }
654                 else
655                 {
656                     if( rEntryID == *apID )
657                     {
658                         pEntry->AddGraphicObjectReference( rObj, rSubstitute );
659                         bInserted = sal_True;
660                     }
661                 }
662 
663                 if( !bInserted )
664                     pEntry = static_cast< GraphicCacheEntry* >( maGraphicCache.Next() );
665             }
666         }
667 	}
668 
669 	if( !bInserted )
670 		maGraphicCache.Insert( new GraphicCacheEntry( rObj ), LIST_APPEND );
671 }
672 
673 // -----------------------------------------------------------------------------
674 
675 void GraphicCache::ReleaseGraphicObject( const GraphicObject& rObj )
676 {
677 	// Release cached object
678 	GraphicCacheEntry*	pEntry = (GraphicCacheEntry*) maGraphicCache.First();
679 	sal_Bool				bRemoved = sal_False;
680 
681 	while( !bRemoved && pEntry )
682 	{
683 		bRemoved = pEntry->ReleaseGraphicObjectReference( rObj );
684 
685 		if( bRemoved )
686 		{
687 			if( 0 == pEntry->GetGraphicObjectReferenceCount() )
688 			{
689 				// if graphic cache entry has no more references,
690 				// the corresponding display cache object can be removed
691 				GraphicDisplayCacheEntry* pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.First();
692 
693 				while( pDisplayEntry )
694 				{
695 					if( pDisplayEntry->GetReferencedCacheEntry() == pEntry )
696 					{
697 						mnUsedDisplaySize -= pDisplayEntry->GetCacheSize();
698 						maDisplayCache.Remove( pDisplayEntry );
699 						delete pDisplayEntry;
700 						pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.GetCurObject();
701 					}
702 					else
703 						pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.Next();
704 				}
705 
706 				// delete graphic cache entry
707 				maGraphicCache.Remove( (void*) pEntry );
708 				delete pEntry;
709 			}
710 		}
711 		else
712 			pEntry = (GraphicCacheEntry*) maGraphicCache.Next();
713 	}
714 
715 	DBG_ASSERT( bRemoved, "GraphicCache::ReleaseGraphicObject(...): GraphicObject not found in cache" );
716 }
717 
718 // -----------------------------------------------------------------------------
719 
720 void GraphicCache::GraphicObjectWasSwappedOut( const GraphicObject& rObj )
721 {
722     // notify cache that rObj is swapped out (and can thus be pruned
723     // from the cache)
724 	GraphicCacheEntry* pEntry = ImplGetCacheEntry( rObj );
725 
726     if( pEntry )
727         pEntry->GraphicObjectWasSwappedOut( rObj );
728 }
729 
730 // -----------------------------------------------------------------------------
731 
732 sal_Bool GraphicCache::FillSwappedGraphicObject( const GraphicObject& rObj, Graphic& rSubstitute )
733 {
734 	GraphicCacheEntry* pEntry = ImplGetCacheEntry( rObj );
735 
736     if( !pEntry )
737         return sal_False;
738 
739 	return pEntry->FillSwappedGraphicObject( rObj, rSubstitute );
740 }
741 
742 // -----------------------------------------------------------------------------
743 
744 void GraphicCache::GraphicObjectWasSwappedIn( const GraphicObject& rObj )
745 {
746 	GraphicCacheEntry* pEntry = ImplGetCacheEntry( rObj );
747 
748     if( pEntry )
749     {
750 	    if( pEntry->GetID().IsEmpty() )
751 	    {
752 		    ReleaseGraphicObject( rObj );
753 		    AddGraphicObject( rObj, (Graphic&) rObj.GetGraphic(), NULL, NULL );
754 	    }
755 	    else
756 		    pEntry->GraphicObjectWasSwappedIn( rObj );
757     }
758 }
759 
760 // -----------------------------------------------------------------------------
761 
762 void GraphicCache::SetMaxDisplayCacheSize( sal_uLong nNewCacheSize )
763 {
764 	mnMaxDisplaySize = nNewCacheSize;
765 
766 	if( GetMaxDisplayCacheSize() < GetUsedDisplayCacheSize() )
767 		ImplFreeDisplayCacheSpace( GetUsedDisplayCacheSize() - GetMaxDisplayCacheSize() );
768 }
769 
770 // -----------------------------------------------------------------------------
771 
772 void GraphicCache::SetMaxObjDisplayCacheSize( sal_uLong nNewMaxObjSize, sal_Bool bDestroyGreaterCached )
773 {
774 	const sal_Bool bDestroy = ( bDestroyGreaterCached && ( nNewMaxObjSize < mnMaxObjDisplaySize ) );
775 
776 	mnMaxObjDisplaySize = Min( nNewMaxObjSize, mnMaxDisplaySize );
777 
778 	if( bDestroy )
779 	{
780 		GraphicDisplayCacheEntry* pCacheObj = (GraphicDisplayCacheEntry*) maDisplayCache.First();
781 
782 		while( pCacheObj )
783 		{
784 			if( pCacheObj->GetCacheSize() > mnMaxObjDisplaySize )
785 			{
786 				mnUsedDisplaySize -= pCacheObj->GetCacheSize();
787 				maDisplayCache.Remove( pCacheObj );
788 				delete pCacheObj;
789 				pCacheObj = (GraphicDisplayCacheEntry*) maDisplayCache.GetCurObject();
790 			}
791 			else
792 				pCacheObj = (GraphicDisplayCacheEntry*) maDisplayCache.Next();
793 		}
794 	}
795 }
796 
797 // -----------------------------------------------------------------------------
798 
799 void GraphicCache::SetCacheTimeout( sal_uLong nTimeoutSeconds )
800 {
801     if( mnReleaseTimeoutSeconds != nTimeoutSeconds )
802     {
803         GraphicDisplayCacheEntry*   pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.First();
804         ::vos::TTimeValue           aReleaseTime;
805 
806         if( ( mnReleaseTimeoutSeconds = nTimeoutSeconds ) != 0 )
807         {
808             osl_getSystemTime( &aReleaseTime );
809             aReleaseTime.addTime( ::vos::TTimeValue( nTimeoutSeconds, 0 ) );
810         }
811 
812 	    while( pDisplayEntry )
813 	    {
814             pDisplayEntry->SetReleaseTime( aReleaseTime );
815 		    pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.Next();
816 	    }
817     }
818 }
819 
820 // -----------------------------------------------------------------------------
821 
822 void GraphicCache::ClearDisplayCache()
823 {
824 	for( void* pObj = maDisplayCache.First(); pObj; pObj = maDisplayCache.Next() )
825 		delete (GraphicDisplayCacheEntry*) pObj;
826 
827 	maDisplayCache.Clear();
828 	mnUsedDisplaySize = 0UL;
829 }
830 
831 // -----------------------------------------------------------------------------
832 
833 sal_Bool GraphicCache::IsDisplayCacheable( OutputDevice* pOut, const Point& rPt, const Size& rSz,
834 									   const GraphicObject& rObj, const GraphicAttr& rAttr ) const
835 {
836 	return( GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) <=
837 			GetMaxObjDisplayCacheSize() );
838 }
839 
840 // -----------------------------------------------------------------------------
841 
842 sal_Bool GraphicCache::IsInDisplayCache( OutputDevice* pOut, const Point& rPt, const Size& rSz,
843 									 const GraphicObject& rObj, const GraphicAttr& rAttr ) const
844 {
845 	const Point					aPtPixel( pOut->LogicToPixel( rPt ) );
846 	const Size					aSzPixel( pOut->LogicToPixel( rSz ) );
847 	const GraphicCacheEntry*	pCacheEntry = ( (GraphicCache*) this )->ImplGetCacheEntry( rObj );
848 	//GraphicDisplayCacheEntry*	pDisplayEntry = (GraphicDisplayCacheEntry*) ( (GraphicCache*) this )->maDisplayCache.First(); // -Wall removed ....
849 	sal_Bool						bFound = sal_False;
850 
851     if( pCacheEntry )
852     {
853         for( long i = 0, nCount = maDisplayCache.Count(); !bFound && ( i < nCount ); i++ )
854             if( ( (GraphicDisplayCacheEntry*) maDisplayCache.GetObject( i ) )->Matches( pOut, aPtPixel, aSzPixel, pCacheEntry, rAttr ) )
855                 bFound = sal_True;
856     }
857 
858 	return bFound;
859 }
860 
861 // -----------------------------------------------------------------------------
862 
863 ByteString GraphicCache::GetUniqueID( const GraphicObject& rObj ) const
864 {
865 	ByteString			aRet;
866 	GraphicCacheEntry*	pEntry = ( (GraphicCache*) this )->ImplGetCacheEntry( rObj );
867 
868 	// ensure that the entry is correctly initialized (it has to be read at least once)
869 	if( pEntry && pEntry->GetID().IsEmpty() )
870 		pEntry->TryToSwapIn();
871 
872 	// do another call to ImplGetCacheEntry in case of modified entry list
873 	pEntry = ( (GraphicCache*) this )->ImplGetCacheEntry( rObj );
874 
875 	if( pEntry )
876 		aRet = pEntry->GetID().GetIDString();
877 
878 	return aRet;
879 }
880 
881 // -----------------------------------------------------------------------------
882 
883 sal_Bool GraphicCache::CreateDisplayCacheObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
884 										  const GraphicObject& rObj, const GraphicAttr& rAttr,
885 										  const BitmapEx& rBmpEx )
886 {
887 	const sal_uLong nNeededSize = GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr );
888 	sal_Bool		bRet = sal_False;
889 
890 	if( nNeededSize <= GetMaxObjDisplayCacheSize() )
891 	{
892 		if( nNeededSize > GetFreeDisplayCacheSize() )
893 			ImplFreeDisplayCacheSpace( nNeededSize - GetFreeDisplayCacheSize() );
894 
895 		GraphicDisplayCacheEntry* pNewEntry = new GraphicDisplayCacheEntry( ImplGetCacheEntry( rObj ),
896 																			pOut, rPt, rSz, rObj, rAttr, rBmpEx );
897 
898         if( GetCacheTimeout() )
899         {
900             ::vos::TTimeValue aReleaseTime;
901 
902             osl_getSystemTime( &aReleaseTime );
903             aReleaseTime.addTime( ::vos::TTimeValue( GetCacheTimeout(), 0 ) );
904             pNewEntry->SetReleaseTime( aReleaseTime );
905         }
906 
907 		maDisplayCache.Insert( pNewEntry, LIST_APPEND );
908 		mnUsedDisplaySize += pNewEntry->GetCacheSize();
909 		bRet = sal_True;
910 	}
911 
912 	return bRet;
913 }
914 
915 // -----------------------------------------------------------------------------
916 
917 sal_Bool GraphicCache::CreateDisplayCacheObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
918 										  const GraphicObject& rObj, const GraphicAttr& rAttr,
919 										  const GDIMetaFile& rMtf )
920 {
921 	const sal_uLong nNeededSize = GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr );
922 	sal_Bool		bRet = sal_False;
923 
924 	if( nNeededSize <= GetMaxObjDisplayCacheSize() )
925 	{
926 		if( nNeededSize > GetFreeDisplayCacheSize() )
927 			ImplFreeDisplayCacheSpace( nNeededSize - GetFreeDisplayCacheSize() );
928 
929 		GraphicDisplayCacheEntry* pNewEntry = new GraphicDisplayCacheEntry( ImplGetCacheEntry( rObj ),
930 																			pOut, rPt, rSz, rObj, rAttr, rMtf );
931 
932         if( GetCacheTimeout() )
933         {
934             ::vos::TTimeValue aReleaseTime;
935 
936             osl_getSystemTime( &aReleaseTime );
937             aReleaseTime.addTime( ::vos::TTimeValue( GetCacheTimeout(), 0 ) );
938             pNewEntry->SetReleaseTime( aReleaseTime );
939         }
940 
941 		maDisplayCache.Insert( pNewEntry, LIST_APPEND );
942 		mnUsedDisplaySize += pNewEntry->GetCacheSize();
943 		bRet = sal_True;
944 	}
945 
946 	return bRet;
947 }
948 
949 // -----------------------------------------------------------------------------
950 
951 sal_Bool GraphicCache::DrawDisplayCacheObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
952 										const GraphicObject& rObj, const GraphicAttr& rAttr )
953 {
954 	const Point					aPtPixel( pOut->LogicToPixel( rPt ) );
955 	const Size					aSzPixel( pOut->LogicToPixel( rSz ) );
956 	const GraphicCacheEntry*	pCacheEntry = ImplGetCacheEntry( rObj );
957 	GraphicDisplayCacheEntry*	pDisplayCacheEntry = (GraphicDisplayCacheEntry*) maDisplayCache.First();
958 	sal_Bool						bRet = sal_False;
959 
960 	while( !bRet && pDisplayCacheEntry )
961 	{
962 		if( pDisplayCacheEntry->Matches( pOut, aPtPixel, aSzPixel, pCacheEntry, rAttr ) )
963 		{
964             ::vos::TTimeValue aReleaseTime;
965 
966 			// put found object at last used position
967 			maDisplayCache.Insert( maDisplayCache.Remove( pDisplayCacheEntry ), LIST_APPEND );
968 
969             if( GetCacheTimeout() )
970             {
971                 osl_getSystemTime( &aReleaseTime );
972                 aReleaseTime.addTime( ::vos::TTimeValue( GetCacheTimeout(), 0 ) );
973             }
974 
975             pDisplayCacheEntry->SetReleaseTime( aReleaseTime );
976 			bRet = sal_True;
977 		}
978 		else
979 			pDisplayCacheEntry = (GraphicDisplayCacheEntry*) maDisplayCache.Next();
980 	}
981 
982 	if( bRet )
983 		pDisplayCacheEntry->Draw( pOut, rPt, rSz );
984 
985 	return bRet;
986 }
987 
988 // -----------------------------------------------------------------------------
989 
990 sal_Bool GraphicCache::ImplFreeDisplayCacheSpace( sal_uLong nSizeToFree )
991 {
992 	sal_uLong nFreedSize = 0UL;
993 
994 	if( nSizeToFree )
995 	{
996 		void* pObj = maDisplayCache.First();
997 
998         if( nSizeToFree > mnUsedDisplaySize )
999             nSizeToFree = mnUsedDisplaySize;
1000 
1001 		while( pObj )
1002 		{
1003 			GraphicDisplayCacheEntry* pCacheObj = (GraphicDisplayCacheEntry*) pObj;
1004 
1005 			nFreedSize += pCacheObj->GetCacheSize();
1006 			mnUsedDisplaySize -= pCacheObj->GetCacheSize();
1007 			maDisplayCache.Remove( pObj );
1008 			delete pCacheObj;
1009 
1010 			if( nFreedSize >= nSizeToFree )
1011 				break;
1012 			else
1013 				pObj = maDisplayCache.GetCurObject();
1014 		}
1015 	}
1016 
1017 	return( nFreedSize >= nSizeToFree );
1018 }
1019 
1020 // -----------------------------------------------------------------------------
1021 
1022 GraphicCacheEntry* GraphicCache::ImplGetCacheEntry( const GraphicObject& rObj )
1023 {
1024 	GraphicCacheEntry* pRet = NULL;
1025 
1026 	for( void* pObj = maGraphicCache.First(); !pRet && pObj; pObj = maGraphicCache.Next() )
1027 		if( ( (GraphicCacheEntry*) pObj )->HasGraphicObjectReference( rObj ) )
1028 			pRet = (GraphicCacheEntry*) pObj;
1029 
1030 	return pRet;
1031 }
1032 
1033 // -----------------------------------------------------------------------------
1034 
1035 IMPL_LINK( GraphicCache, ReleaseTimeoutHdl, Timer*, pTimer )
1036 {
1037     pTimer->Stop();
1038 
1039     ::vos::TTimeValue           aCurTime;
1040 	GraphicDisplayCacheEntry*   pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.First();
1041 
1042     osl_getSystemTime( &aCurTime );
1043 
1044 	while( pDisplayEntry )
1045 	{
1046         const ::vos::TTimeValue& rReleaseTime = pDisplayEntry->GetReleaseTime();
1047 
1048 		if( !rReleaseTime.isEmpty() && ( rReleaseTime < aCurTime ) )
1049 		{
1050 			mnUsedDisplaySize -= pDisplayEntry->GetCacheSize();
1051 			maDisplayCache.Remove( pDisplayEntry );
1052 			delete pDisplayEntry;
1053 			pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.GetCurObject();
1054 		}
1055 		else
1056 			pDisplayEntry = (GraphicDisplayCacheEntry*) maDisplayCache.Next();
1057 	}
1058 
1059     pTimer->Start();
1060 
1061     return 0;
1062 }
1063