xref: /trunk/main/svtools/source/graphic/grfcache.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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