1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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"
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' };
51 GraphicID( const GraphicObject
& rObj
);
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();
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();
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();
117 mnID2
= mnID3
= mnID4
= 0;
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
147 GraphicObjectList_impl maGraphicObjectList
;
153 Animation
* mpAnimation
;
157 SvgDataPtr maSvgData
;
159 bool ImplInit( const GraphicObject
& rObj
);
160 void ImplFillSubstitute( Graphic
& rSubstitute
);
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
);
175 void GraphicObjectWasSwappedOut( const GraphicObject
& rObj
);
176 void GraphicObjectWasSwappedIn( const GraphicObject
& rObj
);
179 GraphicCacheEntry::GraphicCacheEntry( const GraphicObject
& rObj
) :
183 mpAnimation ( NULL
),
184 mbSwappedAll ( true )
186 mbSwappedAll
= !ImplInit( rObj
);
187 maGraphicObjectList
.push_back( const_cast<GraphicObject
*>(&rObj
) );
190 GraphicCacheEntry::~GraphicCacheEntry()
193 maGraphicObjectList
.empty(),
194 "GraphicCacheEntry::~GraphicCacheEntry(): Not all GraphicObjects are removed from this entry"
202 bool GraphicCacheEntry::ImplInit( const GraphicObject
& rObj
)
206 if( !rObj
.IsSwappedOut() )
208 const Graphic
& rGraphic
= rObj
.GetGraphic();
211 delete mpBmpEx
, mpBmpEx
= NULL
;
214 delete mpMtf
, mpMtf
= NULL
;
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() );
233 mpBmpEx
= new BitmapEx( rGraphic
.GetBitmapEx() );
238 case( GRAPHIC_GDIMETAFILE
):
240 mpMtf
= new GDIMetaFile( rGraphic
.GetGDIMetaFile() );
245 DBG_ASSERT( GetID().IsEmpty(), "GraphicCacheEntry::ImplInit: Could not initialize graphic! (=>KA)" );
249 if( rGraphic
.IsLink() )
250 maGfxLink
= ( (Graphic
&) rGraphic
).GetLink();
252 maGfxLink
= GfxLink();
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();
274 rSubstitute
= maSvgData
;
278 rSubstitute
= *mpBmpEx
;
280 else if( mpAnimation
)
282 rSubstitute
= *mpAnimation
;
286 rSubstitute
= *mpMtf
;
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
);
307 rSubstitute
.SetDefaultType();
311 void GraphicCacheEntry::AddGraphicObjectReference( const GraphicObject
& rObj
, Graphic
& rSubstitute
)
314 mbSwappedAll
= !ImplInit( rObj
);
316 ImplFillSubstitute( rSubstitute
);
317 maGraphicObjectList
.push_back( const_cast<GraphicObject
*>(&rObj
) );
320 bool GraphicCacheEntry::ReleaseGraphicObjectReference( const GraphicObject
& rObj
)
323 GraphicObjectList_impl::iterator it
= maGraphicObjectList
.begin();
324 it
!= maGraphicObjectList
.end();
329 maGraphicObjectList
.erase( it
);
337 bool GraphicCacheEntry::HasGraphicObjectReference( const GraphicObject
& rObj
)
341 for( size_t i
= 0, n
= maGraphicObjectList
.size(); ( i
< n
) && !bRet
; ++i
)
342 if( &rObj
== maGraphicObjectList
[ i
] )
348 void GraphicCacheEntry::TryToSwapIn()
350 if( mbSwappedAll
&& !maGraphicObjectList
.empty() )
351 maGraphicObjectList
.front()->FireSwapInRequest();
354 void GraphicCacheEntry::GraphicObjectWasSwappedOut( const GraphicObject
& /*rObj*/ )
358 for( size_t i
= 0, n
= maGraphicObjectList
.size(); ( i
< n
) && mbSwappedAll
; ++i
)
359 if( !maGraphicObjectList
[ i
]->IsSwappedOut() )
360 mbSwappedAll
= false;
364 delete mpBmpEx
, mpBmpEx
= NULL
;
365 delete mpMtf
, mpMtf
= NULL
;
366 delete mpAnimation
, mpAnimation
= NULL
;
368 // #119176# also reset SvgData
373 void GraphicCacheEntry::GraphicObjectWasSwappedIn( const GraphicObject
& rObj
)
376 mbSwappedAll
= !ImplInit( rObj
);
379 class GraphicDisplayCacheEntry
383 ::salhelper::TTimeValue maReleaseTime
;
384 const GraphicCacheEntry
* mpRefCacheEntry
;
389 sal_uLong mnCacheSize
;
390 DrawModeFlags mnOutDevDrawMode
;
391 sal_uInt16 mnOutDevBitCount
;
393 static bool IsCacheableAsBitmap( const GDIMetaFile
& rMtf
, OutputDevice
* pOut
, const Size
& rSz
);
397 static sal_uLong
GetNeededSize( OutputDevice
* pOut
, const Point
& rPt
, const Size
& rSz
,
398 const GraphicObject
& rObj
, const GraphicAttr
& rAttr
);
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
,
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;
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
480 { // fall back to mtf rendering
481 o_rbNonBitmapActionEncountered
= true;
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
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
) );
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
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
:
545 // bitmap output methods
546 case MetaActionType::BMP
:
547 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
549 MetaBmpAction
* pAction
= static_cast<MetaBmpAction
*>(pAct
);
552 BitmapEx( pAction
->GetBitmap()),
554 pOut
->LogicToPixel( pAction
->GetPoint(),
556 pAction
->GetBitmap().GetSizePixel(),
558 bNonBitmapActionEncountered
);
563 case MetaActionType::BMPSCALE
:
564 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
566 MetaBmpScaleAction
* pAction
= static_cast<MetaBmpScaleAction
*>(pAct
);
569 BitmapEx( pAction
->GetBitmap()),
571 pOut
->LogicToPixel( pAction
->GetPoint(),
573 pOut
->LogicToPixel( pAction
->GetSize(),
576 bNonBitmapActionEncountered
);
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(),
591 pOut
->LogicToPixel( pAction
->GetDestSize(),
594 bNonBitmapActionEncountered
);
599 case MetaActionType::BMPEX
:
600 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
602 MetaBmpExAction
* pAction
= static_cast<MetaBmpExAction
*>(pAct
);
605 pAction
->GetBitmapEx(),
607 pOut
->LogicToPixel( pAction
->GetPoint(),
609 pAction
->GetBitmapEx().GetSizePixel(),
611 bNonBitmapActionEncountered
);
616 case MetaActionType::BMPEXSCALE
:
617 if( !nNumBitmaps
&& !bNonBitmapActionEncountered
)
619 MetaBmpExScaleAction
* pAction
= static_cast<MetaBmpExScaleAction
*>(pAct
);
622 pAction
->GetBitmapEx(),
624 pOut
->LogicToPixel( pAction
->GetPoint(),
626 pOut
->LogicToPixel( pAction
->GetSize(),
629 bNonBitmapActionEncountered
);
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(),
644 pOut
->LogicToPixel( pAction
->GetDestSize(),
647 bNonBitmapActionEncountered
);
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
)
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
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
731 bNonBitmapActionEncountered
= true;
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
);
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
) )
764 sal_uLong nNeededSize
= aOutSizePix
.Width() * aOutSizePix
.Height() * nBitCount
/ 8;
765 if( rObj
.IsTransparent() || ( rAttr
.GetRotation() % 3600 ) )
766 nNeededSize
+= nNeededSize
/ nBitCount
;
771 OSL_FAIL( "GraphicDisplayCacheEntry::GetNeededSize(): pOut->GetBitCount() == 0" );
776 return rGraphic
.GetSizeBytes();
779 GraphicDisplayCacheEntry::~GraphicDisplayCacheEntry()
788 void GraphicDisplayCacheEntry::Draw( OutputDevice
* pOut
, const Point
& rPt
, const Size
& rSz
) const
791 GraphicManager::ImplDraw( pOut
, rPt
, rSz
, *mpMtf
, maAttr
);
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
);
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
,
829 const GraphicObject
* pCopyObj
832 bool bInserted
= false;
834 if( !rObj
.IsSwappedOut()
837 && ( pCopyObj
->GetType() != GRAPHIC_NONE
)
839 || ( rObj
.GetType() != GRAPHIC_NONE
)
844 && !maGraphicCache
.empty()
847 GraphicCacheEntryList::iterator it
= maGraphicCache
.begin();
849 && ( it
!= maGraphicCache
.end() )
852 if( (*it
)->HasGraphicObjectReference( *pCopyObj
) )
854 (*it
)->AddGraphicObjectReference( rObj
, rSubstitute
);
866 GraphicCacheEntryList::iterator it
= maGraphicCache
.begin();
867 boost::scoped_ptr
< GraphicID
> apID
;
871 apID
.reset( new GraphicID( rObj
) );
875 && ( it
!= maGraphicCache
.end() )
878 const GraphicID
& rEntryID
= (*it
)->GetID();
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();
894 const GraphicID
& rID
= (*jt
)->GetID();
896 if( rID
.GetIDString() == *pID
)
898 (*jt
)->AddGraphicObjectReference( rObj
, rSubstitute
);
905 maGraphicCache
.push_back( new GraphicCacheEntry( rObj
) );
912 if( rEntryID
== *apID
)
914 (*it
)->AddGraphicObjectReference( rObj
, rSubstitute
);
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
;
956 // delete graphic cache entry
958 it
= maGraphicCache
.erase( 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
971 GraphicCacheEntry
* pEntry
= ImplGetCacheEntry( rObj
);
974 pEntry
->GraphicObjectWasSwappedOut( rObj
);
977 void GraphicCache::GraphicObjectWasSwappedIn( const GraphicObject
& rObj
)
979 GraphicCacheEntry
* pEntry
= ImplGetCacheEntry( rObj
);
983 if( pEntry
->GetID().IsEmpty() )
985 ReleaseGraphicObject( rObj
);
986 AddGraphicObject( rObj
, (Graphic
&) rObj
.GetGraphic(), NULL
, NULL
);
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
);
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
);
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;
1062 for( GraphicDisplayCacheEntryList::const_iterator it
= maDisplayCache
.begin();
1063 !bFound
&& ( it
!= maDisplayCache
.end() ); ++it
)
1065 if( (*it
)->Matches( pOut
, aPtPixel
, aSzPixel
, pCacheEntry
, rAttr
) )
1073 OString
GraphicCache::GetUniqueID( const GraphicObject
& rObj
) const
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
);
1086 aRet
= pEntry
->GetID().GetIDString();
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
);
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();
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
);
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();
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();
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
);
1190 pDisplayCacheEntry
->Draw( pOut
, rPt
, rSz
);
1195 bool GraphicCache::ImplFreeDisplayCacheSpace( sal_uLong nSizeToFree
)
1197 sal_uLong nFreedSize
= 0UL;
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
);
1215 if( nFreedSize
>= nSizeToFree
)
1220 return( nFreedSize
>= nSizeToFree
);
1223 GraphicCacheEntry
* GraphicCache::ImplGetCacheEntry( const GraphicObject
& rObj
)
1225 GraphicCacheEntry
* pRet
= NULL
;
1228 GraphicCacheEntryList::iterator it
= maGraphicCache
.begin();
1229 !pRet
&& it
!= maGraphicCache
.end();
1232 if( (*it
)->HasGraphicObjectReference( rObj
) ) {
1240 IMPL_LINK_TYPED( GraphicCache
, ReleaseTimeoutHdl
, Timer
*, pTimer
, void )
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
;
1267 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */