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 <vcl/animate.hxx>
21 #include <tools/stream.hxx>
23 #include <vcl/virdev.hxx>
24 #include <vcl/window.hxx>
25 #include <vcl/dibtools.hxx>
27 #include "impanmvw.hxx"
32 sal_uLong
Animation::mnAnimCount
= 0UL;
34 BitmapChecksum
AnimationBitmap::GetChecksum() const
36 BitmapChecksum nCrc
= aBmpEx
.GetChecksum();
39 UInt32ToSVBT32( aPosPix
.X(), aBT32
);
40 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
42 UInt32ToSVBT32( aPosPix
.Y(), aBT32
);
43 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
45 UInt32ToSVBT32( aSizePix
.Width(), aBT32
);
46 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
48 UInt32ToSVBT32( aSizePix
.Height(), aBT32
);
49 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
51 UInt32ToSVBT32( (long) nWait
, aBT32
);
52 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
54 UInt32ToSVBT32( (long) eDisposal
, aBT32
);
55 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
57 UInt32ToSVBT32( (long) bUserInput
, aBT32
);
58 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
63 Animation::Animation() :
67 mbIsInAnimation ( false ),
68 mbLoopTerminated ( false )
70 maTimer
.SetInvokeHandler( LINK( this, Animation
, ImplTimeoutHdl
) );
73 Animation::Animation( const Animation
& rAnimation
) :
74 maBitmapEx ( rAnimation
.maBitmapEx
),
75 maGlobalSize ( rAnimation
.maGlobalSize
),
76 mnLoopCount ( rAnimation
.mnLoopCount
),
77 mnPos ( rAnimation
.mnPos
),
78 mbIsInAnimation ( false ),
79 mbLoopTerminated ( rAnimation
.mbLoopTerminated
)
82 for(const AnimationBitmap
* i
: rAnimation
.maList
)
83 maList
.push_back( new AnimationBitmap( *i
) );
85 maTimer
.SetInvokeHandler( LINK( this, Animation
, ImplTimeoutHdl
) );
86 mnLoops
= mbLoopTerminated
? 0 : mnLoopCount
;
89 Animation::~Animation()
95 for(AnimationBitmap
* i
: maList
)
98 for(ImplAnimView
* i
: maViewList
)
102 Animation
& Animation::operator=( const Animation
& rAnimation
)
106 for(const AnimationBitmap
* i
: rAnimation
.maList
)
107 maList
.push_back( new AnimationBitmap( *i
) );
109 maGlobalSize
= rAnimation
.maGlobalSize
;
110 maBitmapEx
= rAnimation
.maBitmapEx
;
111 mnLoopCount
= rAnimation
.mnLoopCount
;
112 mnPos
= rAnimation
.mnPos
;
113 mbLoopTerminated
= rAnimation
.mbLoopTerminated
;
114 mnLoops
= mbLoopTerminated
? 0 : mnLoopCount
;
119 bool Animation::operator==( const Animation
& rAnimation
) const
121 const size_t nCount
= maList
.size();
124 if( rAnimation
.maList
.size() == nCount
125 && rAnimation
.maBitmapEx
== maBitmapEx
126 && rAnimation
.maGlobalSize
== maGlobalSize
131 for( size_t n
= 0; n
< nCount
; n
++ )
133 if( ( *maList
[ n
] ) != ( *rAnimation
.maList
[ n
] ) )
144 void Animation::Clear()
147 mbIsInAnimation
= false;
148 maGlobalSize
= Size();
149 maBitmapEx
.SetEmpty();
151 for(AnimationBitmap
* i
: maList
)
155 for(ImplAnimView
* i
: maViewList
)
160 bool Animation::IsTransparent() const
163 tools::Rectangle
aRect( aPoint
, maGlobalSize
);
166 // If some small bitmap needs to be replaced by the background,
167 // we need to be transparent, in order to be displayed correctly
168 // as the application (?) does not invalidate on non-transparent
169 // graphics due to performance reasons.
170 for(const AnimationBitmap
* pAnimBmp
: maList
)
172 if( Disposal::Back
== pAnimBmp
->eDisposal
173 && tools::Rectangle( pAnimBmp
->aPosPix
, pAnimBmp
->aSizePix
) != aRect
182 bRet
= maBitmapEx
.IsTransparent();
187 sal_uLong
Animation::GetSizeBytes() const
189 sal_uLong nSizeBytes
= GetBitmapEx().GetSizeBytes();
191 for(const AnimationBitmap
* pAnimBmp
: maList
)
193 nSizeBytes
+= pAnimBmp
->aBmpEx
.GetSizeBytes();
199 BitmapChecksum
Animation::GetChecksum() const
202 BitmapChecksumOctetArray aBCOA
;
203 BitmapChecksum nCrc
= GetBitmapEx().GetChecksum();
205 UInt32ToSVBT32( maList
.size(), aBT32
);
206 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
208 UInt32ToSVBT32( maGlobalSize
.Width(), aBT32
);
209 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
211 UInt32ToSVBT32( maGlobalSize
.Height(), aBT32
);
212 nCrc
= vcl_get_checksum( nCrc
, aBT32
, 4 );
214 for(const AnimationBitmap
* i
: maList
)
216 BCToBCOA( i
->GetChecksum(), aBCOA
);
217 nCrc
= vcl_get_checksum( nCrc
, aBCOA
, BITMAP_CHECKSUM_SIZE
);
223 bool Animation::Start( OutputDevice
* pOut
, const Point
& rDestPt
, const Size
& rDestSz
, long nExtraData
,
224 OutputDevice
* pFirstFrameOutDev
)
228 if( !maList
.empty() )
230 if( ( pOut
->GetOutDevType() == OUTDEV_WINDOW
)
232 && ( ANIMATION_TIMEOUT_ON_CLICK
!= maList
[ mnPos
]->nWait
)
236 ImplAnimView
* pMatch
= nullptr;
238 for( size_t i
= 0; i
< maViewList
.size(); ++i
)
240 pView
= maViewList
[ i
];
241 if( pView
->matches( pOut
, nExtraData
) )
243 if( pView
->getOutPos() == rDestPt
&&
244 pView
->getOutSizePix() == pOut
->LogicToPixel( rDestSz
) )
251 delete maViewList
[ i
];
252 maViewList
.erase( maViewList
.begin() + i
);
260 if( maViewList
.empty() )
263 mbIsInAnimation
= false;
268 maViewList
.push_back( new ImplAnimView( this, pOut
, rDestPt
, rDestSz
, nExtraData
, pFirstFrameOutDev
) );
270 if( !mbIsInAnimation
)
272 ImplRestartTimer( maList
[ mnPos
]->nWait
);
273 mbIsInAnimation
= true;
277 Draw( pOut
, rDestPt
, rDestSz
);
285 void Animation::Stop( OutputDevice
* pOut
, long nExtraData
)
287 for( size_t i
= 0; i
< maViewList
.size(); )
290 ImplAnimView
* pView
= maViewList
[ i
];
291 if( pView
->matches( pOut
, nExtraData
) )
294 maViewList
.erase( maViewList
.begin() + i
);
300 if( maViewList
.empty() )
303 mbIsInAnimation
= false;
307 void Animation::Draw( OutputDevice
* pOut
, const Point
& rDestPt
) const
309 Draw( pOut
, rDestPt
, pOut
->PixelToLogic( maGlobalSize
) );
312 void Animation::Draw( OutputDevice
* pOut
, const Point
& rDestPt
, const Size
& rDestSz
) const
314 const size_t nCount
= maList
.size();
318 AnimationBitmap
* pObj
= maList
[ std::min( mnPos
, nCount
- 1 ) ];
320 if( pOut
->GetConnectMetaFile()
321 || ( pOut
->GetOutDevType() == OUTDEV_PRINTER
)
323 maList
[ 0 ]->aBmpEx
.Draw( pOut
, rDestPt
, rDestSz
);
324 else if( ANIMATION_TIMEOUT_ON_CLICK
== pObj
->nWait
)
325 pObj
->aBmpEx
.Draw( pOut
, rDestPt
, rDestSz
);
328 const size_t nOldPos
= mnPos
;
329 const_cast<Animation
*>(this)->mnPos
= mbLoopTerminated
? ( nCount
- 1UL ) : mnPos
;
330 delete new ImplAnimView( const_cast<Animation
*>(this), pOut
, rDestPt
, rDestSz
, 0 );
331 const_cast<Animation
*>(this)->mnPos
= nOldPos
;
336 void Animation::ImplRestartTimer( sal_uLong nTimeout
)
338 maTimer
.SetTimeout( std::max( nTimeout
, (sal_uLong
)(MIN_TIMEOUT
+ ( mnAnimCount
- 1 ) * INC_TIMEOUT
) ) * 10 );
342 IMPL_LINK_NOARG(Animation
, ImplTimeoutHdl
, Timer
*, void)
344 const size_t nAnimCount
= maList
.size();
345 std::vector
< AInfo
* > aAInfoList
;
350 bool bGlobalPause
= true;
352 if( maNotifyLink
.IsSet() )
355 for(ImplAnimView
* i
: maViewList
)
356 aAInfoList
.push_back( i
->createAInfo() );
358 maNotifyLink
.Call( this );
360 // set view state from AInfo structure
361 for(AInfo
* pAInfo
: aAInfoList
)
363 if( !pAInfo
->pViewData
)
365 pView
= new ImplAnimView( this, pAInfo
->pOutDev
,
366 pAInfo
->aStartOrg
, pAInfo
->aStartSize
, pAInfo
->nExtraData
);
368 maViewList
.push_back( pView
);
371 pView
= static_cast<ImplAnimView
*>(pAInfo
->pViewData
);
373 pView
->pause( pAInfo
->bPause
);
374 pView
->setMarked( true );
377 // delete AInfo structures
378 for(AInfo
* i
: aAInfoList
)
382 // delete all unmarked views and reset marked state
383 for( size_t i
= 0; i
< maViewList
.size(); )
385 pView
= maViewList
[ i
];
386 if( !pView
->isMarked() )
389 maViewList
.erase( maViewList
.begin() + i
);
393 if( !pView
->isPause() )
394 bGlobalPause
= false;
396 pView
->setMarked( false );
402 bGlobalPause
= false;
404 if( maViewList
.empty() )
406 else if( bGlobalPause
)
407 ImplRestartTimer( 10 );
410 AnimationBitmap
* pStepBmp
= (++mnPos
< maList
.size()) ? maList
[ mnPos
] : nullptr;
417 mbLoopTerminated
= true;
418 mnPos
= nAnimCount
- 1UL;
419 maBitmapEx
= maList
[ mnPos
]->aBmpEx
;
428 pStepBmp
= maList
[ mnPos
];
432 // Paint all views; after painting check, if view is
433 // marked; in this case remove view, because area of output
434 // lies out of display area of window; mark state is
435 // set from view itself
436 for( size_t i
= 0; i
< maViewList
.size(); )
438 pView
= maViewList
[ i
];
439 pView
->draw( mnPos
);
441 if( pView
->isMarked() )
444 maViewList
.erase( maViewList
.begin() + i
);
450 // stop or restart timer
451 if( maViewList
.empty() )
454 ImplRestartTimer( pStepBmp
->nWait
);
461 bool Animation::Insert( const AnimationBitmap
& rStepBmp
)
465 if( !IsInAnimation() )
468 tools::Rectangle
aGlobalRect( aPoint
, maGlobalSize
);
470 maGlobalSize
= aGlobalRect
.Union( tools::Rectangle( rStepBmp
.aPosPix
, rStepBmp
.aSizePix
) ).GetSize();
471 maList
.push_back( new AnimationBitmap( rStepBmp
) );
473 // As a start, we make the first BitmapEx the replacement BitmapEx
474 if( maList
.size() == 1 )
475 maBitmapEx
= rStepBmp
.aBmpEx
;
483 const AnimationBitmap
& Animation::Get( sal_uInt16 nAnimation
) const
485 SAL_WARN_IF( ( nAnimation
>= maList
.size() ), "vcl", "No object at this position" );
486 return *maList
[ nAnimation
];
489 void Animation::Replace( const AnimationBitmap
& rNewAnimationBitmap
, sal_uInt16 nAnimation
)
491 SAL_WARN_IF( ( nAnimation
>= maList
.size() ), "vcl", "No object at this position" );
493 delete maList
[ nAnimation
];
494 maList
[ nAnimation
] = new AnimationBitmap( rNewAnimationBitmap
);
496 // If we insert at first position we also need to
497 // update the replacement BitmapEx
499 && ( !mbLoopTerminated
500 || ( maList
.size() == 1 )
504 ( ( nAnimation
== maList
.size() - 1 )
509 maBitmapEx
= rNewAnimationBitmap
.aBmpEx
;
513 void Animation::SetLoopCount( const sal_uLong nLoopCount
)
515 mnLoopCount
= nLoopCount
;
519 void Animation::ResetLoopCount()
521 mnLoops
= mnLoopCount
;
522 mbLoopTerminated
= false;
525 bool Animation::Convert( BmpConversion eConversion
)
527 SAL_WARN_IF( IsInAnimation(), "vcl", "Animation modified while it is animated" );
531 if( !IsInAnimation() && !maList
.empty() )
535 for( size_t i
= 0, n
= maList
.size(); ( i
< n
) && bRet
; ++i
)
536 bRet
= maList
[ i
]->aBmpEx
.Convert( eConversion
);
538 maBitmapEx
.Convert( eConversion
);
546 bool Animation::ReduceColors( sal_uInt16 nNewColorCount
)
548 SAL_WARN_IF( IsInAnimation(), "vcl", "Animation modified while it is animated" );
552 if( !IsInAnimation() && !maList
.empty() )
556 for( size_t i
= 0, n
= maList
.size(); ( i
< n
) && bRet
; ++i
)
557 bRet
= maList
[ i
]->aBmpEx
.ReduceColors( nNewColorCount
);
559 maBitmapEx
.ReduceColors( nNewColorCount
);
567 bool Animation::Invert()
569 SAL_WARN_IF( IsInAnimation(), "vcl", "Animation modified while it is animated" );
573 if( !IsInAnimation() && !maList
.empty() )
577 for( size_t i
= 0, n
= maList
.size(); ( i
< n
) && bRet
; ++i
)
578 bRet
= maList
[ i
]->aBmpEx
.Invert();
588 bool Animation::Mirror( BmpMirrorFlags nMirrorFlags
)
590 SAL_WARN_IF( IsInAnimation(), "vcl", "Animation modified while it is animated" );
594 if( !IsInAnimation() && !maList
.empty() )
598 if( nMirrorFlags
!= BmpMirrorFlags::NONE
)
600 for( size_t i
= 0, n
= maList
.size(); ( i
< n
) && bRet
; ++i
)
602 AnimationBitmap
* pStepBmp
= maList
[ i
];
603 bRet
= pStepBmp
->aBmpEx
.Mirror( nMirrorFlags
);
606 if( nMirrorFlags
& BmpMirrorFlags::Horizontal
)
607 pStepBmp
->aPosPix
.X() = maGlobalSize
.Width() - pStepBmp
->aPosPix
.X() - pStepBmp
->aSizePix
.Width();
609 if( nMirrorFlags
& BmpMirrorFlags::Vertical
)
610 pStepBmp
->aPosPix
.Y() = maGlobalSize
.Height() - pStepBmp
->aPosPix
.Y() - pStepBmp
->aSizePix
.Height();
614 maBitmapEx
.Mirror( nMirrorFlags
);
623 bool Animation::Adjust( short nLuminancePercent
, short nContrastPercent
,
624 short nChannelRPercent
, short nChannelGPercent
, short nChannelBPercent
,
625 double fGamma
, bool bInvert
)
627 SAL_WARN_IF( IsInAnimation(), "vcl", "Animation modified while it is animated" );
631 if( !IsInAnimation() && !maList
.empty() )
635 for( size_t i
= 0, n
= maList
.size(); ( i
< n
) && bRet
; ++i
)
637 bRet
= maList
[ i
]->aBmpEx
.Adjust( nLuminancePercent
,
646 maBitmapEx
.Adjust( nLuminancePercent
, nContrastPercent
,
647 nChannelRPercent
, nChannelGPercent
, nChannelBPercent
,
656 bool Animation::Filter( BmpFilter eFilter
, const BmpFilterParam
* pFilterParam
)
658 SAL_WARN_IF( IsInAnimation(), "vcl", "Animation modified while it is animated" );
662 if( !IsInAnimation() && !maList
.empty() )
666 for( size_t i
= 0, n
= maList
.size(); ( i
< n
) && bRet
; ++i
)
667 bRet
= maList
[ i
]->aBmpEx
.Filter( eFilter
, pFilterParam
);
669 (void)maBitmapEx
.Filter(eFilter
, pFilterParam
);
677 SvStream
& WriteAnimation( SvStream
& rOStm
, const Animation
& rAnimation
)
679 const sal_uInt16 nCount
= rAnimation
.Count();
683 const sal_uInt32 nDummy32
= 0UL;
685 // If no BitmapEx was set we write the first Bitmap of
687 if( !rAnimation
.GetBitmapEx().GetBitmap() )
688 WriteDIBBitmapEx(rAnimation
.Get( 0 ).aBmpEx
, rOStm
);
690 WriteDIBBitmapEx(rAnimation
.GetBitmapEx(), rOStm
);
692 // Write identifier ( SDANIMA1 )
693 rOStm
.WriteUInt32( 0x5344414e ).WriteUInt32( 0x494d4931 );
695 for( sal_uInt16 i
= 0; i
< nCount
; i
++ )
697 const AnimationBitmap
& rAnimBmp
= rAnimation
.Get( i
);
698 const sal_uInt16 nRest
= nCount
- i
- 1;
700 // Write AnimationBitmap
701 WriteDIBBitmapEx(rAnimBmp
.aBmpEx
, rOStm
);
702 WritePair( rOStm
, rAnimBmp
.aPosPix
);
703 WritePair( rOStm
, rAnimBmp
.aSizePix
);
704 WritePair( rOStm
, rAnimation
.maGlobalSize
);
705 rOStm
.WriteUInt16( ( ANIMATION_TIMEOUT_ON_CLICK
== rAnimBmp
.nWait
) ? 65535 : rAnimBmp
.nWait
);
706 rOStm
.WriteUInt16( (sal_uInt16
)rAnimBmp
.eDisposal
);
707 rOStm
.WriteBool( rAnimBmp
.bUserInput
);
708 rOStm
.WriteUInt32( rAnimation
.mnLoopCount
);
709 rOStm
.WriteUInt32( nDummy32
); // Unused
710 rOStm
.WriteUInt32( nDummy32
); // Unused
711 rOStm
.WriteUInt32( nDummy32
); // Unused
712 write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm
, OString()); // dummy
713 rOStm
.WriteUInt16( nRest
); // Count of remaining structures
720 SvStream
& ReadAnimation( SvStream
& rIStm
, Animation
& rAnimation
)
724 sal_uInt32 nAnimMagic1
, nAnimMagic2
;
725 SvStreamEndian nOldFormat
= rIStm
.GetEndian();
726 bool bReadAnimations
= false;
728 rIStm
.SetEndian( SvStreamEndian::LITTLE
);
729 nStmPos
= rIStm
.Tell();
730 rIStm
.ReadUInt32( nAnimMagic1
).ReadUInt32( nAnimMagic2
);
734 // If the BitmapEx at the beginning have already been read (by Graphic)
735 // we can start reading the AnimationBitmaps right away
736 if( ( nAnimMagic1
== 0x5344414e ) && ( nAnimMagic2
== 0x494d4931 ) && !rIStm
.GetError() )
737 bReadAnimations
= true;
738 // Else, we try reading the Bitmap(-Ex)
741 rIStm
.Seek( nStmPos
);
742 ReadDIBBitmapEx(rAnimation
.maBitmapEx
, rIStm
);
743 nStmPos
= rIStm
.Tell();
744 rIStm
.ReadUInt32( nAnimMagic1
).ReadUInt32( nAnimMagic2
);
746 if( ( nAnimMagic1
== 0x5344414e ) && ( nAnimMagic2
== 0x494d4931 ) && !rIStm
.GetError() )
747 bReadAnimations
= true;
749 rIStm
.Seek( nStmPos
);
752 // Read AnimationBitmaps
753 if( bReadAnimations
)
755 AnimationBitmap aAnimBmp
;
763 ReadDIBBitmapEx(aAnimBmp
.aBmpEx
, rIStm
);
764 ReadPair( rIStm
, aAnimBmp
.aPosPix
);
765 ReadPair( rIStm
, aAnimBmp
.aSizePix
);
766 ReadPair( rIStm
, rAnimation
.maGlobalSize
);
767 rIStm
.ReadUInt16( nTmp16
); aAnimBmp
.nWait
= ( ( 65535 == nTmp16
) ? ANIMATION_TIMEOUT_ON_CLICK
: nTmp16
);
768 rIStm
.ReadUInt16( nTmp16
); aAnimBmp
.eDisposal
= ( Disposal
) nTmp16
;
769 rIStm
.ReadCharAsBool( cTmp
); aAnimBmp
.bUserInput
= cTmp
;
770 rIStm
.ReadUInt32( nTmp32
); rAnimation
.mnLoopCount
= (sal_uInt16
) nTmp32
;
771 rIStm
.ReadUInt32( nTmp32
); // Unused
772 rIStm
.ReadUInt32( nTmp32
); // Unused
773 rIStm
.ReadUInt32( nTmp32
); // Unused
774 read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm
); // Unused
775 rIStm
.ReadUInt16( nTmp16
); // The rest to read
777 rAnimation
.Insert( aAnimBmp
);
779 while( nTmp16
&& !rIStm
.GetError() );
781 rAnimation
.ResetLoopCount();
784 rIStm
.SetEndian( nOldFormat
);
789 AInfo::AInfo() : pOutDev( nullptr ),
790 pViewData( nullptr ),
794 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */