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