Bump version to 5.0-14
[LibreOffice.git] / svtools / source / graphic / grfcache.cxx
blob28e4ab0a457ec748d859d47fb6c8911c232b678e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cstdlib>
24 #include <salhelper/timer.hxx>
25 #include <svtools/grfmgr.hxx>
26 #include <tools/debug.hxx>
27 #include <vcl/metaact.hxx>
28 #include <vcl/outdev.hxx>
29 #include <tools/poly.hxx>
30 #include <rtl/strbuf.hxx>
31 #include "grfcache.hxx"
32 #include <rtl/crc.h>
33 #include <boost/scoped_ptr.hpp>
35 #define MAX_BMP_EXTENT 4096
37 static const char aHexData[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
39 class GraphicID
41 private:
43 sal_uInt32 mnID1;
44 sal_uInt32 mnID2;
45 sal_uInt32 mnID3;
46 BitmapChecksum mnID4;
48 public:
51 GraphicID( const GraphicObject& rObj );
52 ~GraphicID() {}
54 bool operator==( const GraphicID& rID ) const
56 return( rID.mnID1 == mnID1 && rID.mnID2 == mnID2 &&
57 rID.mnID3 == mnID3 && rID.mnID4 == mnID4 );
60 OString GetIDString() const;
61 bool IsEmpty() const { return( 0 == mnID4 ); }
64 GraphicID::GraphicID( const GraphicObject& rObj )
66 const Graphic& rGraphic = rObj.GetGraphic();
68 mnID1 = ( (sal_uLong) rGraphic.GetType() ) << 28;
70 switch( rGraphic.GetType() )
72 case( GRAPHIC_BITMAP ):
74 if(rGraphic.getSvgData().get())
76 const SvgDataPtr& rSvgDataPtr = rGraphic.getSvgData();
77 const basegfx::B2DRange& rRange = rSvgDataPtr->getRange();
79 mnID1 |= rSvgDataPtr->getSvgDataArrayLength();
80 mnID2 = basegfx::fround(rRange.getWidth());
81 mnID3 = basegfx::fround(rRange.getHeight());
82 mnID4 = vcl_get_checksum(0, rSvgDataPtr->getSvgDataArray().get(), rSvgDataPtr->getSvgDataArrayLength());
84 else if( rGraphic.IsAnimated() )
86 const Animation aAnimation( rGraphic.GetAnimation() );
88 mnID1 |= ( aAnimation.Count() & 0x0fffffff );
89 mnID2 = aAnimation.GetDisplaySizePixel().Width();
90 mnID3 = aAnimation.GetDisplaySizePixel().Height();
91 mnID4 = rGraphic.GetChecksum();
93 else
95 const BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
97 mnID1 |= ( ( ( (sal_uLong) aBmpEx.GetTransparentType() << 8 ) | ( aBmpEx.IsAlpha() ? 1 : 0 ) ) & 0x0fffffff );
98 mnID2 = aBmpEx.GetSizePixel().Width();
99 mnID3 = aBmpEx.GetSizePixel().Height();
100 mnID4 = rGraphic.GetChecksum();
103 break;
105 case( GRAPHIC_GDIMETAFILE ):
107 const GDIMetaFile& rMtf = rGraphic.GetGDIMetaFile();
109 mnID1 |= ( rMtf.GetActionSize() & 0x0fffffff );
110 mnID2 = rMtf.GetPrefSize().Width();
111 mnID3 = rMtf.GetPrefSize().Height();
112 mnID4 = rGraphic.GetChecksum();
114 break;
116 default:
117 mnID2 = mnID3 = mnID4 = 0;
118 break;
122 OString GraphicID::GetIDString() const
124 OStringBuffer aHexStr;
125 sal_Int32 nShift, nIndex = 0;
126 aHexStr.setLength(24 + (2 * BITMAP_CHECKSUM_SIZE));
128 for( nShift = 28; nShift >= 0; nShift -= 4 )
129 aHexStr[nIndex++] = aHexData[ ( mnID1 >> (sal_uInt32) nShift ) & 0xf ];
131 for( nShift = 28; nShift >= 0; nShift -= 4 )
132 aHexStr[nIndex++] = aHexData[ ( mnID2 >> (sal_uInt32) nShift ) & 0xf ];
134 for( nShift = 28; nShift >= 0; nShift -= 4 )
135 aHexStr[nIndex++] = aHexData[ ( mnID3 >> (sal_uInt32) nShift ) & 0xf ];
137 for( nShift = BITMAP_CHECKSUM_BITS - 4; nShift >= 0; nShift -= 4 )
138 aHexStr[nIndex++] = aHexData[ ( mnID4 >> (sal_uInt32) nShift ) & 0xf ];
140 return aHexStr.makeStringAndClear();
143 class GraphicCacheEntry
145 private:
147 GraphicObjectList_impl maGraphicObjectList;
149 GraphicID maID;
150 GfxLink maGfxLink;
151 BitmapEx* mpBmpEx;
152 GDIMetaFile* mpMtf;
153 Animation* mpAnimation;
154 bool mbSwappedAll;
156 // SvgData support
157 SvgDataPtr maSvgData;
159 bool ImplInit( const GraphicObject& rObj );
160 void ImplFillSubstitute( Graphic& rSubstitute );
162 public:
164 GraphicCacheEntry( const GraphicObject& rObj );
165 ~GraphicCacheEntry();
167 const GraphicID& GetID() const { return maID; }
169 void AddGraphicObjectReference( const GraphicObject& rObj, Graphic& rSubstitute );
170 bool ReleaseGraphicObjectReference( const GraphicObject& rObj );
171 size_t GetGraphicObjectReferenceCount() { return maGraphicObjectList.size(); }
172 bool HasGraphicObjectReference( const GraphicObject& rObj );
174 void TryToSwapIn();
175 void GraphicObjectWasSwappedOut( const GraphicObject& rObj );
176 void GraphicObjectWasSwappedIn( const GraphicObject& rObj );
179 GraphicCacheEntry::GraphicCacheEntry( const GraphicObject& rObj ) :
180 maID ( rObj ),
181 mpBmpEx ( NULL ),
182 mpMtf ( NULL ),
183 mpAnimation ( NULL ),
184 mbSwappedAll ( true )
186 mbSwappedAll = !ImplInit( rObj );
187 maGraphicObjectList.push_back( const_cast<GraphicObject*>(&rObj) );
190 GraphicCacheEntry::~GraphicCacheEntry()
192 DBG_ASSERT(
193 maGraphicObjectList.empty(),
194 "GraphicCacheEntry::~GraphicCacheEntry(): Not all GraphicObjects are removed from this entry"
197 delete mpBmpEx;
198 delete mpMtf;
199 delete mpAnimation;
202 bool GraphicCacheEntry::ImplInit( const GraphicObject& rObj )
204 bool bRet = false;
206 if( !rObj.IsSwappedOut() )
208 const Graphic& rGraphic = rObj.GetGraphic();
210 if( mpBmpEx )
211 delete mpBmpEx, mpBmpEx = NULL;
213 if( mpMtf )
214 delete mpMtf, mpMtf = NULL;
216 if( mpAnimation )
217 delete mpAnimation, mpAnimation = NULL;
219 switch( rGraphic.GetType() )
221 case( GRAPHIC_BITMAP ):
223 if(rGraphic.getSvgData().get())
225 maSvgData = rGraphic.getSvgData();
227 else if( rGraphic.IsAnimated() )
229 mpAnimation = new Animation( rGraphic.GetAnimation() );
231 else
233 mpBmpEx = new BitmapEx( rGraphic.GetBitmapEx() );
236 break;
238 case( GRAPHIC_GDIMETAFILE ):
240 mpMtf = new GDIMetaFile( rGraphic.GetGDIMetaFile() );
242 break;
244 default:
245 DBG_ASSERT( GetID().IsEmpty(), "GraphicCacheEntry::ImplInit: Could not initialize graphic! (=>KA)" );
246 break;
249 if( rGraphic.IsLink() )
250 maGfxLink = ( (Graphic&) rGraphic ).GetLink();
251 else
252 maGfxLink = GfxLink();
254 bRet = true;
257 return bRet;
260 void GraphicCacheEntry::ImplFillSubstitute( Graphic& rSubstitute )
262 // create substitute for graphic;
263 const Size aPrefSize( rSubstitute.GetPrefSize() );
264 const MapMode aPrefMapMode( rSubstitute.GetPrefMapMode() );
265 const Link<> aAnimationNotifyHdl( rSubstitute.GetAnimationNotifyHdl() );
266 const GraphicType eOldType = rSubstitute.GetType();
267 const bool bDefaultType = ( rSubstitute.GetType() == GRAPHIC_DEFAULT );
269 if( rSubstitute.IsLink() && ( GFX_LINK_TYPE_NONE == maGfxLink.GetType() ) )
270 maGfxLink = rSubstitute.GetLink();
272 if(maSvgData.get())
274 rSubstitute = maSvgData;
276 else if( mpBmpEx )
278 rSubstitute = *mpBmpEx;
280 else if( mpAnimation )
282 rSubstitute = *mpAnimation;
284 else if( mpMtf )
286 rSubstitute = *mpMtf;
288 else
290 rSubstitute.Clear();
293 if( eOldType != GRAPHIC_NONE )
295 rSubstitute.SetPrefSize( aPrefSize );
296 rSubstitute.SetPrefMapMode( aPrefMapMode );
297 rSubstitute.SetAnimationNotifyHdl( aAnimationNotifyHdl );
300 if( GFX_LINK_TYPE_NONE != maGfxLink.GetType() )
302 rSubstitute.SetLink( maGfxLink );
305 if( bDefaultType )
307 rSubstitute.SetDefaultType();
311 void GraphicCacheEntry::AddGraphicObjectReference( const GraphicObject& rObj, Graphic& rSubstitute )
313 if( mbSwappedAll )
314 mbSwappedAll = !ImplInit( rObj );
316 ImplFillSubstitute( rSubstitute );
317 maGraphicObjectList.push_back( const_cast<GraphicObject*>(&rObj) );
320 bool GraphicCacheEntry::ReleaseGraphicObjectReference( const GraphicObject& rObj )
322 for(
323 GraphicObjectList_impl::iterator it = maGraphicObjectList.begin();
324 it != maGraphicObjectList.end();
325 ++it
327 if( &rObj == *it )
329 maGraphicObjectList.erase( it );
330 return true;
334 return false;
337 bool GraphicCacheEntry::HasGraphicObjectReference( const GraphicObject& rObj )
339 bool bRet = false;
341 for( size_t i = 0, n = maGraphicObjectList.size(); ( i < n ) && !bRet; ++i )
342 if( &rObj == maGraphicObjectList[ i ] )
343 bRet = true;
345 return bRet;
348 void GraphicCacheEntry::TryToSwapIn()
350 if( mbSwappedAll && !maGraphicObjectList.empty() )
351 maGraphicObjectList.front()->FireSwapInRequest();
354 void GraphicCacheEntry::GraphicObjectWasSwappedOut( const GraphicObject& /*rObj*/ )
356 mbSwappedAll = true;
358 for( size_t i = 0, n = maGraphicObjectList.size(); ( i < n ) && mbSwappedAll; ++i )
359 if( !maGraphicObjectList[ i ]->IsSwappedOut() )
360 mbSwappedAll = false;
362 if( mbSwappedAll )
364 delete mpBmpEx, mpBmpEx = NULL;
365 delete mpMtf, mpMtf = NULL;
366 delete mpAnimation, mpAnimation = NULL;
368 // #119176# also reset SvgData
369 maSvgData.reset();
373 void GraphicCacheEntry::GraphicObjectWasSwappedIn( const GraphicObject& rObj )
375 if( mbSwappedAll )
376 mbSwappedAll = !ImplInit( rObj );
379 class GraphicDisplayCacheEntry
381 private:
383 ::salhelper::TTimeValue maReleaseTime;
384 const GraphicCacheEntry* mpRefCacheEntry;
385 GDIMetaFile* mpMtf;
386 BitmapEx* mpBmpEx;
387 GraphicAttr maAttr;
388 Size maOutSizePix;
389 sal_uLong mnCacheSize;
390 DrawModeFlags mnOutDevDrawMode;
391 sal_uInt16 mnOutDevBitCount;
393 static bool IsCacheableAsBitmap( const GDIMetaFile& rMtf, OutputDevice* pOut, const Size& rSz );
395 public:
397 static sal_uLong GetNeededSize( OutputDevice* pOut, const Point& rPt, const Size& rSz,
398 const GraphicObject& rObj, const GraphicAttr& rAttr );
400 public:
402 GraphicDisplayCacheEntry( const GraphicCacheEntry* pRefCacheEntry,
403 OutputDevice* pOut, const Point& rPt, const Size& rSz,
404 const GraphicObject& rObj, const GraphicAttr& rAttr,
405 const BitmapEx& rBmpEx ) :
406 mpRefCacheEntry( pRefCacheEntry ),
407 mpMtf( NULL ), mpBmpEx( new BitmapEx( rBmpEx ) ),
408 maAttr( rAttr ), maOutSizePix( pOut->LogicToPixel( rSz ) ),
409 mnCacheSize( GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) ),
410 mnOutDevDrawMode( pOut->GetDrawMode() ),
411 mnOutDevBitCount( pOut->GetBitCount() )
415 GraphicDisplayCacheEntry( const GraphicCacheEntry* pRefCacheEntry,
416 OutputDevice* pOut, const Point& rPt, const Size& rSz,
417 const GraphicObject& rObj, const GraphicAttr& rAttr,
418 const GDIMetaFile& rMtf ) :
419 mpRefCacheEntry( pRefCacheEntry ),
420 mpMtf( new GDIMetaFile( rMtf ) ), mpBmpEx( NULL ),
421 maAttr( rAttr ), maOutSizePix( pOut->LogicToPixel( rSz ) ),
422 mnCacheSize( GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) ),
423 mnOutDevDrawMode( pOut->GetDrawMode() ),
424 mnOutDevBitCount( pOut->GetBitCount() )
429 ~GraphicDisplayCacheEntry();
431 sal_uLong GetCacheSize() const { return mnCacheSize; }
432 const GraphicCacheEntry* GetReferencedCacheEntry() const { return mpRefCacheEntry; }
434 void SetReleaseTime( const ::salhelper::TTimeValue& rReleaseTime ) { maReleaseTime = rReleaseTime; }
435 const ::salhelper::TTimeValue& GetReleaseTime() const { return maReleaseTime; }
437 bool Matches( OutputDevice* pOut, const Point& /*rPtPixel*/, const Size& rSzPixel,
438 const GraphicCacheEntry* pCacheEntry, const GraphicAttr& rAttr ) const
440 // #i46805# Additional match
441 // criteria: outdev draw mode and
442 // bit count. One cannot reuse
443 // this cache object, if it's
444 // e.g. generated for
445 // DrawModeFlags::GrayBitmap.
446 return( ( pCacheEntry == mpRefCacheEntry ) &&
447 ( maAttr == rAttr ) &&
448 ( ( maOutSizePix == rSzPixel ) || ( !maOutSizePix.Width() && !maOutSizePix.Height() ) ) &&
449 ( pOut->GetBitCount() == mnOutDevBitCount ) &&
450 ( pOut->GetDrawMode() == mnOutDevDrawMode ) );
453 void Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz ) const;
456 // This whole function is based on checkMetadataBitmap() from grfmgr2.cxx, see that one for details.
457 // If you do changes here, change the original function too.
458 static void checkMetadataBitmap( const BitmapEx& rBmpEx,
459 Point /*rSrcPoint*/,
460 Size rSrcSize,
461 const Point& rDestPoint,
462 const Size& rDestSize,
463 const Size& rRefSize,
464 bool& o_rbNonBitmapActionEncountered )
466 if( rSrcSize == Size())
467 rSrcSize = rBmpEx.GetSizePixel();
469 if( rDestPoint != Point( 0, 0 ))
471 o_rbNonBitmapActionEncountered = true;
472 return;
474 if( rDestSize != rRefSize )
475 { if( rBmpEx.GetSizePixel().Width() > 100 && rBmpEx.GetSizePixel().Height() > 100
476 && std::abs( rDestSize.Width() - rRefSize.Width()) < 5
477 && std::abs( rDestSize.Height() - rRefSize.Height()) < 5 )
478 ; // ok, assume it's close enough
479 else
480 { // fall back to mtf rendering
481 o_rbNonBitmapActionEncountered = true;
482 return;
487 // This function is based on GraphicManager::ImplCreateOutput(), in fact it mostly copies
488 // it, the difference is that this one does not create anything, it only checks if
489 // ImplCreateOutput() would use the optimization of using the single bitmap.
490 // If you do changes here, change the original function too.
491 bool GraphicDisplayCacheEntry::IsCacheableAsBitmap( const GDIMetaFile& rMtf,
492 OutputDevice* pOut, const Size& rSz )
494 const Size aNewSize( rMtf.GetPrefSize() );
495 GDIMetaFile rOutMtf = rMtf;
497 // Count bitmap actions, and flag actions that paint, but
498 // are no bitmaps.
499 sal_Int32 nNumBitmaps(0);
500 bool bNonBitmapActionEncountered(false);
501 if( aNewSize.Width() && aNewSize.Height() && rSz.Width() && rSz.Height() )
503 const MapMode rPrefMapMode( rMtf.GetPrefMapMode() );
504 const Size rSizePix( pOut->LogicToPixel( aNewSize, rPrefMapMode ) );
506 sal_uInt32 nCurPos;
507 MetaAction* pAct;
508 for( nCurPos = 0, pAct = rOutMtf.FirstAction(); pAct;
509 pAct = rOutMtf.NextAction(), nCurPos++ )
511 switch( pAct->GetType() )
513 case MetaActionType::FONT:
514 // FALLTHROUGH intended
515 case MetaActionType::NONE:
516 // FALLTHROUGH intended
518 // OutDev state changes (which don't affect bitmap
519 // output)
520 case MetaActionType::LINECOLOR:
521 // FALLTHROUGH intended
522 case MetaActionType::FILLCOLOR:
523 // FALLTHROUGH intended
524 case MetaActionType::TEXTCOLOR:
525 // FALLTHROUGH intended
526 case MetaActionType::TEXTFILLCOLOR:
527 // FALLTHROUGH intended
528 case MetaActionType::TEXTALIGN:
529 // FALLTHROUGH intended
530 case MetaActionType::TEXTLINECOLOR:
531 // FALLTHROUGH intended
532 case MetaActionType::TEXTLINE:
533 // FALLTHROUGH intended
534 case MetaActionType::PUSH:
535 // FALLTHROUGH intended
536 case MetaActionType::POP:
537 // FALLTHROUGH intended
538 case MetaActionType::LAYOUTMODE:
539 // FALLTHROUGH intended
540 case MetaActionType::TEXTLANGUAGE:
541 // FALLTHROUGH intended
542 case MetaActionType::COMMENT:
543 break;
545 // bitmap output methods
546 case MetaActionType::BMP:
547 if( !nNumBitmaps && !bNonBitmapActionEncountered )
549 MetaBmpAction* pAction = static_cast<MetaBmpAction*>(pAct);
551 checkMetadataBitmap(
552 BitmapEx( pAction->GetBitmap()),
553 Point(), Size(),
554 pOut->LogicToPixel( pAction->GetPoint(),
555 rPrefMapMode ),
556 pAction->GetBitmap().GetSizePixel(),
557 rSizePix,
558 bNonBitmapActionEncountered );
560 ++nNumBitmaps;
561 break;
563 case MetaActionType::BMPSCALE:
564 if( !nNumBitmaps && !bNonBitmapActionEncountered )
566 MetaBmpScaleAction* pAction = static_cast<MetaBmpScaleAction*>(pAct);
568 checkMetadataBitmap(
569 BitmapEx( pAction->GetBitmap()),
570 Point(), Size(),
571 pOut->LogicToPixel( pAction->GetPoint(),
572 rPrefMapMode ),
573 pOut->LogicToPixel( pAction->GetSize(),
574 rPrefMapMode ),
575 rSizePix,
576 bNonBitmapActionEncountered );
578 ++nNumBitmaps;
579 break;
581 case MetaActionType::BMPSCALEPART:
582 if( !nNumBitmaps && !bNonBitmapActionEncountered )
584 MetaBmpScalePartAction* pAction = static_cast<MetaBmpScalePartAction*>(pAct);
586 checkMetadataBitmap( BitmapEx( pAction->GetBitmap() ),
587 pAction->GetSrcPoint(),
588 pAction->GetSrcSize(),
589 pOut->LogicToPixel( pAction->GetDestPoint(),
590 rPrefMapMode ),
591 pOut->LogicToPixel( pAction->GetDestSize(),
592 rPrefMapMode ),
593 rSizePix,
594 bNonBitmapActionEncountered );
596 ++nNumBitmaps;
597 break;
599 case MetaActionType::BMPEX:
600 if( !nNumBitmaps && !bNonBitmapActionEncountered )
602 MetaBmpExAction* pAction = static_cast<MetaBmpExAction*>(pAct);
604 checkMetadataBitmap(
605 pAction->GetBitmapEx(),
606 Point(), Size(),
607 pOut->LogicToPixel( pAction->GetPoint(),
608 rPrefMapMode ),
609 pAction->GetBitmapEx().GetSizePixel(),
610 rSizePix,
611 bNonBitmapActionEncountered );
613 ++nNumBitmaps;
614 break;
616 case MetaActionType::BMPEXSCALE:
617 if( !nNumBitmaps && !bNonBitmapActionEncountered )
619 MetaBmpExScaleAction* pAction = static_cast<MetaBmpExScaleAction*>(pAct);
621 checkMetadataBitmap(
622 pAction->GetBitmapEx(),
623 Point(), Size(),
624 pOut->LogicToPixel( pAction->GetPoint(),
625 rPrefMapMode ),
626 pOut->LogicToPixel( pAction->GetSize(),
627 rPrefMapMode ),
628 rSizePix,
629 bNonBitmapActionEncountered );
631 ++nNumBitmaps;
632 break;
634 case MetaActionType::BMPEXSCALEPART:
635 if( !nNumBitmaps && !bNonBitmapActionEncountered )
637 MetaBmpExScalePartAction* pAction = static_cast<MetaBmpExScalePartAction*>(pAct);
639 checkMetadataBitmap( pAction->GetBitmapEx(),
640 pAction->GetSrcPoint(),
641 pAction->GetSrcSize(),
642 pOut->LogicToPixel( pAction->GetDestPoint(),
643 rPrefMapMode ),
644 pOut->LogicToPixel( pAction->GetDestSize(),
645 rPrefMapMode ),
646 rSizePix,
647 bNonBitmapActionEncountered );
649 ++nNumBitmaps;
650 break;
652 // these actions actually output something (that's
653 // different from a bitmap)
654 case MetaActionType::RASTEROP:
655 if( static_cast<MetaRasterOpAction*>(pAct)->GetRasterOp() == ROP_OVERPAINT )
656 break;
657 // FALLTHROUGH intended
658 case MetaActionType::PIXEL:
659 // FALLTHROUGH intended
660 case MetaActionType::POINT:
661 // FALLTHROUGH intended
662 case MetaActionType::LINE:
663 // FALLTHROUGH intended
664 case MetaActionType::RECT:
665 // FALLTHROUGH intended
666 case MetaActionType::ROUNDRECT:
667 // FALLTHROUGH intended
668 case MetaActionType::ELLIPSE:
669 // FALLTHROUGH intended
670 case MetaActionType::ARC:
671 // FALLTHROUGH intended
672 case MetaActionType::PIE:
673 // FALLTHROUGH intended
674 case MetaActionType::CHORD:
675 // FALLTHROUGH intended
676 case MetaActionType::POLYLINE:
677 // FALLTHROUGH intended
678 case MetaActionType::POLYGON:
679 // FALLTHROUGH intended
680 case MetaActionType::POLYPOLYGON:
681 // FALLTHROUGH intended
683 case MetaActionType::TEXT:
684 // FALLTHROUGH intended
685 case MetaActionType::TEXTARRAY:
686 // FALLTHROUGH intended
687 case MetaActionType::STRETCHTEXT:
688 // FALLTHROUGH intended
689 case MetaActionType::TEXTRECT:
690 // FALLTHROUGH intended
692 case MetaActionType::MASK:
693 // FALLTHROUGH intended
694 case MetaActionType::MASKSCALE:
695 // FALLTHROUGH intended
696 case MetaActionType::MASKSCALEPART:
697 // FALLTHROUGH intended
699 case MetaActionType::GRADIENT:
700 // FALLTHROUGH intended
701 case MetaActionType::HATCH:
702 // FALLTHROUGH intended
703 case MetaActionType::WALLPAPER:
704 // FALLTHROUGH intended
706 case MetaActionType::Transparent:
707 // FALLTHROUGH intended
708 case MetaActionType::EPS:
709 // FALLTHROUGH intended
710 case MetaActionType::FLOATTRANSPARENT:
711 // FALLTHROUGH intended
712 case MetaActionType::GRADIENTEX:
713 // FALLTHROUGH intended
715 // OutDev state changes that _do_ affect bitmap
716 // output
717 case MetaActionType::CLIPREGION:
718 // FALLTHROUGH intended
719 case MetaActionType::ISECTRECTCLIPREGION:
720 // FALLTHROUGH intended
721 case MetaActionType::ISECTREGIONCLIPREGION:
722 // FALLTHROUGH intended
723 case MetaActionType::MOVECLIPREGION:
724 // FALLTHROUGH intended
726 case MetaActionType::MAPMODE:
727 // FALLTHROUGH intended
728 case MetaActionType::REFPOINT:
729 // FALLTHROUGH intended
730 default:
731 bNonBitmapActionEncountered = true;
732 break;
736 return nNumBitmaps == 1 && !bNonBitmapActionEncountered;
739 sal_uLong GraphicDisplayCacheEntry::GetNeededSize( OutputDevice* pOut, const Point& /*rPt*/, const Size& rSz,
740 const GraphicObject& rObj, const GraphicAttr& rAttr )
742 const Graphic& rGraphic = rObj.GetGraphic();
743 const GraphicType eType = rGraphic.GetType();
745 bool canCacheAsBitmap = false;
746 if( GRAPHIC_BITMAP == eType )
747 canCacheAsBitmap = true;
748 else if( GRAPHIC_GDIMETAFILE == eType )
749 canCacheAsBitmap = IsCacheableAsBitmap( rGraphic.GetGDIMetaFile(), pOut, rSz );
750 else
751 return 0;
752 if( canCacheAsBitmap )
754 const Size aOutSizePix( pOut->LogicToPixel( rSz ) );
755 const long nBitCount = pOut->GetBitCount();
757 if( ( aOutSizePix.Width() > MAX_BMP_EXTENT ) ||
758 ( aOutSizePix.Height() > MAX_BMP_EXTENT ) )
760 return ULONG_MAX;
762 else if( nBitCount )
764 sal_uLong nNeededSize = aOutSizePix.Width() * aOutSizePix.Height() * nBitCount / 8;
765 if( rObj.IsTransparent() || ( rAttr.GetRotation() % 3600 ) )
766 nNeededSize += nNeededSize / nBitCount;
767 return nNeededSize;
769 else
771 OSL_FAIL( "GraphicDisplayCacheEntry::GetNeededSize(): pOut->GetBitCount() == 0" );
772 return 256000;
775 else
776 return rGraphic.GetSizeBytes();
779 GraphicDisplayCacheEntry::~GraphicDisplayCacheEntry()
781 if( mpMtf )
782 delete mpMtf;
784 if( mpBmpEx )
785 delete mpBmpEx;
788 void GraphicDisplayCacheEntry::Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz ) const
790 if( mpMtf )
791 GraphicManager::ImplDraw( pOut, rPt, rSz, *mpMtf, maAttr );
792 else if( mpBmpEx )
794 if( maAttr.IsRotated() )
796 Polygon aPoly( Rectangle( rPt, rSz ) );
798 aPoly.Rotate( rPt, maAttr.GetRotation() % 3600 );
799 const Rectangle aRotBoundRect( aPoly.GetBoundRect() );
800 pOut->DrawBitmapEx( aRotBoundRect.TopLeft(), aRotBoundRect.GetSize(), *mpBmpEx );
802 else
803 pOut->DrawBitmapEx( rPt, rSz, *mpBmpEx );
807 GraphicCache::GraphicCache( sal_uLong nDisplayCacheSize, sal_uLong nMaxObjDisplayCacheSize ) :
808 maReleaseTimer ( "GraphicCache maReleaseTimer" ),
809 mnReleaseTimeoutSeconds ( 0UL ),
810 mnMaxDisplaySize ( nDisplayCacheSize ),
811 mnMaxObjDisplaySize ( nMaxObjDisplayCacheSize ),
812 mnUsedDisplaySize ( 0UL )
814 maReleaseTimer.SetTimeoutHdl( LINK( this, GraphicCache, ReleaseTimeoutHdl ) );
815 maReleaseTimer.SetTimeout( 10000 );
816 maReleaseTimer.Start();
819 GraphicCache::~GraphicCache()
821 DBG_ASSERT( !maGraphicCache.size(), "GraphicCache::~GraphicCache(): there are some GraphicObjects in cache" );
822 DBG_ASSERT( maDisplayCache.empty(), "GraphicCache::~GraphicCache(): there are some GraphicObjects in display cache" );
825 void GraphicCache::AddGraphicObject(
826 const GraphicObject& rObj,
827 Graphic& rSubstitute,
828 const OString* pID,
829 const GraphicObject* pCopyObj
832 bool bInserted = false;
834 if( !rObj.IsSwappedOut()
835 && ( pID
836 || ( pCopyObj
837 && ( pCopyObj->GetType() != GRAPHIC_NONE )
839 || ( rObj.GetType() != GRAPHIC_NONE )
843 if( pCopyObj
844 && !maGraphicCache.empty()
847 GraphicCacheEntryList::iterator it = maGraphicCache.begin();
848 while( !bInserted
849 && ( it != maGraphicCache.end() )
852 if( (*it)->HasGraphicObjectReference( *pCopyObj ) )
854 (*it)->AddGraphicObjectReference( rObj, rSubstitute );
855 bInserted = true;
857 else
859 ++it;
864 if( !bInserted )
866 GraphicCacheEntryList::iterator it = maGraphicCache.begin();
867 boost::scoped_ptr< GraphicID > apID;
869 if( !pID )
871 apID.reset( new GraphicID( rObj ) );
874 while( !bInserted
875 && ( it != maGraphicCache.end() )
878 const GraphicID& rEntryID = (*it)->GetID();
880 if( pID )
882 if( rEntryID.GetIDString() == *pID )
884 (*it)->TryToSwapIn();
886 // since pEntry->TryToSwapIn can modify our current list, we have to
887 // iterate from beginning to add a reference to the appropriate
888 // CacheEntry object; after this, quickly jump out of the outer iteration
889 for( GraphicCacheEntryList::iterator jt = maGraphicCache.begin();
890 !bInserted && jt != maGraphicCache.end();
891 ++jt
894 const GraphicID& rID = (*jt)->GetID();
896 if( rID.GetIDString() == *pID )
898 (*jt)->AddGraphicObjectReference( rObj, rSubstitute );
899 bInserted = true;
903 if( !bInserted )
905 maGraphicCache.push_back( new GraphicCacheEntry( rObj ) );
906 bInserted = true;
910 else
912 if( rEntryID == *apID )
914 (*it)->AddGraphicObjectReference( rObj, rSubstitute );
915 bInserted = true;
919 if( !bInserted )
920 ++it;
925 if( !bInserted )
926 maGraphicCache.push_back( new GraphicCacheEntry( rObj ) );
929 void GraphicCache::ReleaseGraphicObject( const GraphicObject& rObj )
931 // Release cached object
932 bool bRemoved = false;
933 GraphicCacheEntryList::iterator it = maGraphicCache.begin();
934 while (!bRemoved && it != maGraphicCache.end())
936 bRemoved = (*it)->ReleaseGraphicObjectReference( rObj );
938 if( bRemoved && (0 == (*it)->GetGraphicObjectReferenceCount()) )
940 // if graphic cache entry has no more references,
941 // the corresponding display cache object can be removed
942 GraphicDisplayCacheEntryList::iterator it2 = maDisplayCache.begin();
943 while( it2 != maDisplayCache.end() )
945 GraphicDisplayCacheEntry* pDisplayEntry = *it2;
946 if( pDisplayEntry->GetReferencedCacheEntry() == *it )
948 mnUsedDisplaySize -= pDisplayEntry->GetCacheSize();
949 it2 = maDisplayCache.erase( it2 );
950 delete pDisplayEntry;
952 else
953 ++it2;
956 // delete graphic cache entry
957 delete *it;
958 it = maGraphicCache.erase( it );
960 else
961 ++it;
964 DBG_ASSERT( bRemoved, "GraphicCache::ReleaseGraphicObject(...): GraphicObject not found in cache" );
967 void GraphicCache::GraphicObjectWasSwappedOut( const GraphicObject& rObj )
969 // notify cache that rObj is swapped out (and can thus be pruned
970 // from the cache)
971 GraphicCacheEntry* pEntry = ImplGetCacheEntry( rObj );
973 if( pEntry )
974 pEntry->GraphicObjectWasSwappedOut( rObj );
977 void GraphicCache::GraphicObjectWasSwappedIn( const GraphicObject& rObj )
979 GraphicCacheEntry* pEntry = ImplGetCacheEntry( rObj );
981 if( pEntry )
983 if( pEntry->GetID().IsEmpty() )
985 ReleaseGraphicObject( rObj );
986 AddGraphicObject( rObj, (Graphic&) rObj.GetGraphic(), NULL, NULL );
988 else
989 pEntry->GraphicObjectWasSwappedIn( rObj );
993 void GraphicCache::SetMaxDisplayCacheSize( sal_uLong nNewCacheSize )
995 mnMaxDisplaySize = nNewCacheSize;
997 if( GetMaxDisplayCacheSize() < GetUsedDisplayCacheSize() )
998 ImplFreeDisplayCacheSpace( GetUsedDisplayCacheSize() - GetMaxDisplayCacheSize() );
1001 void GraphicCache::SetMaxObjDisplayCacheSize( sal_uLong nNewMaxObjSize, bool bDestroyGreaterCached )
1003 const bool bDestroy = ( bDestroyGreaterCached && ( nNewMaxObjSize < mnMaxObjDisplaySize ) );
1005 mnMaxObjDisplaySize = std::min( nNewMaxObjSize, mnMaxDisplaySize );
1007 if( bDestroy )
1009 GraphicDisplayCacheEntryList::iterator it = maDisplayCache.begin();
1010 while( it != maDisplayCache.end() )
1012 GraphicDisplayCacheEntry* pCacheObj = *it;
1013 if( pCacheObj->GetCacheSize() > mnMaxObjDisplaySize )
1015 mnUsedDisplaySize -= pCacheObj->GetCacheSize();
1016 it = maDisplayCache.erase( it );
1017 delete pCacheObj;
1019 else
1020 ++it;
1025 void GraphicCache::SetCacheTimeout( sal_uLong nTimeoutSeconds )
1027 if( mnReleaseTimeoutSeconds != nTimeoutSeconds )
1029 ::salhelper::TTimeValue aReleaseTime;
1031 if( ( mnReleaseTimeoutSeconds = nTimeoutSeconds ) != 0 )
1033 osl_getSystemTime( &aReleaseTime );
1034 aReleaseTime.addTime( ::salhelper::TTimeValue( nTimeoutSeconds, 0 ) );
1037 for( GraphicDisplayCacheEntryList::const_iterator it = maDisplayCache.begin();
1038 it != maDisplayCache.end(); ++it )
1040 (*it)->SetReleaseTime( aReleaseTime );
1045 bool GraphicCache::IsDisplayCacheable( OutputDevice* pOut, const Point& rPt, const Size& rSz,
1046 const GraphicObject& rObj, const GraphicAttr& rAttr ) const
1048 return( GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) <=
1049 GetMaxObjDisplayCacheSize() );
1052 bool GraphicCache::IsInDisplayCache( OutputDevice* pOut, const Point& rPt, const Size& rSz,
1053 const GraphicObject& rObj, const GraphicAttr& rAttr ) const
1055 const Point aPtPixel( pOut->LogicToPixel( rPt ) );
1056 const Size aSzPixel( pOut->LogicToPixel( rSz ) );
1057 const GraphicCacheEntry* pCacheEntry = const_cast<GraphicCache*>(this)->ImplGetCacheEntry( rObj );
1058 bool bFound = false;
1060 if( pCacheEntry )
1062 for( GraphicDisplayCacheEntryList::const_iterator it = maDisplayCache.begin();
1063 !bFound && ( it != maDisplayCache.end() ); ++it )
1065 if( (*it)->Matches( pOut, aPtPixel, aSzPixel, pCacheEntry, rAttr ) )
1066 bFound = true;
1070 return bFound;
1073 OString GraphicCache::GetUniqueID( const GraphicObject& rObj ) const
1075 OString aRet;
1076 GraphicCacheEntry* pEntry = const_cast<GraphicCache*>(this)->ImplGetCacheEntry( rObj );
1078 // ensure that the entry is correctly initialized (it has to be read at least once)
1079 if( pEntry && pEntry->GetID().IsEmpty() )
1080 pEntry->TryToSwapIn();
1082 // do another call to ImplGetCacheEntry in case of modified entry list
1083 pEntry = const_cast<GraphicCache*>(this)->ImplGetCacheEntry( rObj );
1085 if( pEntry )
1086 aRet = pEntry->GetID().GetIDString();
1088 return aRet;
1091 bool GraphicCache::CreateDisplayCacheObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
1092 const GraphicObject& rObj, const GraphicAttr& rAttr,
1093 const BitmapEx& rBmpEx )
1095 const sal_uLong nNeededSize = GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr );
1096 bool bRet = false;
1098 if( nNeededSize <= GetMaxObjDisplayCacheSize() )
1100 if( nNeededSize > GetFreeDisplayCacheSize() )
1101 ImplFreeDisplayCacheSpace( nNeededSize - GetFreeDisplayCacheSize() );
1103 GraphicDisplayCacheEntry* pNewEntry = new GraphicDisplayCacheEntry( ImplGetCacheEntry( rObj ),
1104 pOut, rPt, rSz, rObj, rAttr, rBmpEx );
1106 if( GetCacheTimeout() )
1108 ::salhelper::TTimeValue aReleaseTime;
1110 osl_getSystemTime( &aReleaseTime );
1111 aReleaseTime.addTime( ::salhelper::TTimeValue( GetCacheTimeout(), 0 ) );
1112 pNewEntry->SetReleaseTime( aReleaseTime );
1115 maDisplayCache.push_back( pNewEntry );
1116 mnUsedDisplaySize += pNewEntry->GetCacheSize();
1117 bRet = true;
1120 return bRet;
1123 bool GraphicCache::CreateDisplayCacheObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
1124 const GraphicObject& rObj, const GraphicAttr& rAttr,
1125 const GDIMetaFile& rMtf )
1127 const sal_uLong nNeededSize = GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr );
1128 bool bRet = false;
1130 if( nNeededSize <= GetMaxObjDisplayCacheSize() )
1132 if( nNeededSize > GetFreeDisplayCacheSize() )
1133 ImplFreeDisplayCacheSpace( nNeededSize - GetFreeDisplayCacheSize() );
1135 GraphicDisplayCacheEntry* pNewEntry = new GraphicDisplayCacheEntry( ImplGetCacheEntry( rObj ),
1136 pOut, rPt, rSz, rObj, rAttr, rMtf );
1138 if( GetCacheTimeout() )
1140 ::salhelper::TTimeValue aReleaseTime;
1142 osl_getSystemTime( &aReleaseTime );
1143 aReleaseTime.addTime( ::salhelper::TTimeValue( GetCacheTimeout(), 0 ) );
1144 pNewEntry->SetReleaseTime( aReleaseTime );
1147 maDisplayCache.push_back( pNewEntry );
1148 mnUsedDisplaySize += pNewEntry->GetCacheSize();
1149 bRet = true;
1152 return bRet;
1155 bool GraphicCache::DrawDisplayCacheObj( OutputDevice* pOut, const Point& rPt, const Size& rSz,
1156 const GraphicObject& rObj, const GraphicAttr& rAttr )
1158 const Point aPtPixel( pOut->LogicToPixel( rPt ) );
1159 const Size aSzPixel( pOut->LogicToPixel( rSz ) );
1160 const GraphicCacheEntry* pCacheEntry = ImplGetCacheEntry( rObj );
1161 GraphicDisplayCacheEntry* pDisplayCacheEntry = NULL;
1162 GraphicDisplayCacheEntryList::iterator it = maDisplayCache.begin();
1163 bool bRet = false;
1165 while( !bRet && it != maDisplayCache.end() )
1167 pDisplayCacheEntry = *it;
1168 if( pDisplayCacheEntry->Matches( pOut, aPtPixel, aSzPixel, pCacheEntry, rAttr ) )
1170 ::salhelper::TTimeValue aReleaseTime;
1172 // put found object at last used position
1173 it = maDisplayCache.erase( it );
1174 maDisplayCache.push_back( pDisplayCacheEntry );
1176 if( GetCacheTimeout() )
1178 osl_getSystemTime( &aReleaseTime );
1179 aReleaseTime.addTime( ::salhelper::TTimeValue( GetCacheTimeout(), 0 ) );
1182 pDisplayCacheEntry->SetReleaseTime( aReleaseTime );
1183 bRet = true;
1185 else
1186 ++it;
1189 if( bRet )
1190 pDisplayCacheEntry->Draw( pOut, rPt, rSz );
1192 return bRet;
1195 bool GraphicCache::ImplFreeDisplayCacheSpace( sal_uLong nSizeToFree )
1197 sal_uLong nFreedSize = 0UL;
1199 if( nSizeToFree )
1201 GraphicDisplayCacheEntryList::iterator it = maDisplayCache.begin();
1203 if( nSizeToFree > mnUsedDisplaySize )
1204 nSizeToFree = mnUsedDisplaySize;
1206 while( it != maDisplayCache.end() )
1208 GraphicDisplayCacheEntry* pCacheObj = *it;
1210 nFreedSize += pCacheObj->GetCacheSize();
1211 mnUsedDisplaySize -= pCacheObj->GetCacheSize();
1212 it = maDisplayCache.erase( it );
1213 delete pCacheObj;
1215 if( nFreedSize >= nSizeToFree )
1216 break;
1220 return( nFreedSize >= nSizeToFree );
1223 GraphicCacheEntry* GraphicCache::ImplGetCacheEntry( const GraphicObject& rObj )
1225 GraphicCacheEntry* pRet = NULL;
1227 for(
1228 GraphicCacheEntryList::iterator it = maGraphicCache.begin();
1229 !pRet && it != maGraphicCache.end();
1230 ++it
1232 if( (*it)->HasGraphicObjectReference( rObj ) ) {
1233 pRet = *it;
1237 return pRet;
1240 IMPL_LINK_TYPED( GraphicCache, ReleaseTimeoutHdl, Timer*, pTimer, void )
1242 pTimer->Stop();
1244 ::salhelper::TTimeValue aCurTime;
1245 GraphicDisplayCacheEntryList::iterator it = maDisplayCache.begin();
1247 osl_getSystemTime( &aCurTime );
1249 while( it != maDisplayCache.end() )
1251 GraphicDisplayCacheEntry* pDisplayEntry = *it;
1252 const ::salhelper::TTimeValue& rReleaseTime = pDisplayEntry->GetReleaseTime();
1254 if( !rReleaseTime.isEmpty() && ( rReleaseTime < aCurTime ) )
1256 mnUsedDisplaySize -= pDisplayEntry->GetCacheSize();
1257 it = maDisplayCache.erase( it );
1258 delete pDisplayEntry;
1260 else
1261 ++it;
1264 pTimer->Start();
1267 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */