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