nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / animate / Animation.cxx
blob69984bbe456641d97becac6b1fd8b8145b26f981
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 <algorithm>
21 #include <sal/config.h>
23 #include <tools/stream.hxx>
24 #include <tools/GenericTypeSerializer.hxx>
25 #include <sal/log.hxx>
27 #include <vcl/animate/Animation.hxx>
28 #include <vcl/outdev.hxx>
29 #include <vcl/dibtools.hxx>
30 #include <vcl/BitmapColorQuantizationFilter.hxx>
32 #include <impanmvw.hxx>
34 sal_uLong Animation::mnAnimCount = 0;
36 Animation::Animation()
37 : mnLoopCount(0)
38 , mnLoops(0)
39 , mnPos(0)
40 , mbIsInAnimation(false)
41 , mbLoopTerminated(false)
43 maTimer.SetInvokeHandler(LINK(this, Animation, ImplTimeoutHdl));
46 Animation::Animation(const Animation& rAnimation)
47 : maBitmapEx(rAnimation.maBitmapEx)
48 , maGlobalSize(rAnimation.maGlobalSize)
49 , mnLoopCount(rAnimation.mnLoopCount)
50 , mnPos(rAnimation.mnPos)
51 , mbIsInAnimation(false)
52 , mbLoopTerminated(rAnimation.mbLoopTerminated)
54 for (auto const& i : rAnimation.maList)
55 maList.emplace_back(new AnimationBitmap(*i));
57 maTimer.SetInvokeHandler(LINK(this, Animation, ImplTimeoutHdl));
58 mnLoops = mbLoopTerminated ? 0 : mnLoopCount;
61 Animation::~Animation()
63 if (mbIsInAnimation)
64 Stop();
67 Animation& Animation::operator=(const Animation& rAnimation)
69 if (this != &rAnimation)
71 Clear();
73 for (auto const& i : rAnimation.maList)
74 maList.emplace_back(new AnimationBitmap(*i));
76 maGlobalSize = rAnimation.maGlobalSize;
77 maBitmapEx = rAnimation.maBitmapEx;
78 mnLoopCount = rAnimation.mnLoopCount;
79 mnPos = rAnimation.mnPos;
80 mbLoopTerminated = rAnimation.mbLoopTerminated;
81 mnLoops = mbLoopTerminated ? 0 : mnLoopCount;
83 return *this;
86 bool Animation::operator==(const Animation& rAnimation) const
88 return maList.size() == rAnimation.maList.size() && maBitmapEx == rAnimation.maBitmapEx
89 && maGlobalSize == rAnimation.maGlobalSize
90 && std::equal(maList.begin(), maList.end(), rAnimation.maList.begin(),
91 [](const std::unique_ptr<AnimationBitmap>& pAnim1,
92 const std::unique_ptr<AnimationBitmap>& pAnim2) -> bool {
93 return *pAnim1 == *pAnim2;
94 });
97 void Animation::Clear()
99 maTimer.Stop();
100 mbIsInAnimation = false;
101 maGlobalSize = Size();
102 maBitmapEx.SetEmpty();
103 maList.clear();
104 maViewList.clear();
107 bool Animation::IsTransparent() const
109 tools::Rectangle aRect{ Point(), maGlobalSize };
111 // If some small bitmap needs to be replaced by the background,
112 // we need to be transparent, in order to be displayed correctly
113 // as the application (?) does not invalidate on non-transparent
114 // graphics due to performance reasons.
116 return maBitmapEx.IsTransparent()
117 || std::any_of(maList.begin(), maList.end(),
118 [&aRect](const std::unique_ptr<AnimationBitmap>& pAnim) -> bool {
119 return pAnim->meDisposal == Disposal::Back
120 && tools::Rectangle{ pAnim->maPositionPixel,
121 pAnim->maSizePixel }
122 != aRect;
126 sal_uLong Animation::GetSizeBytes() const
128 sal_uLong nSizeBytes = GetBitmapEx().GetSizeBytes();
130 for (auto const& pAnimationBitmap : maList)
132 nSizeBytes += pAnimationBitmap->maBitmapEx.GetSizeBytes();
135 return nSizeBytes;
138 BitmapChecksum Animation::GetChecksum() const
140 SVBT32 aBT32;
141 BitmapChecksumOctetArray aBCOA;
142 BitmapChecksum nCrc = GetBitmapEx().GetChecksum();
144 UInt32ToSVBT32(maList.size(), aBT32);
145 nCrc = vcl_get_checksum(nCrc, aBT32, 4);
147 Int32ToSVBT32(maGlobalSize.Width(), aBT32);
148 nCrc = vcl_get_checksum(nCrc, aBT32, 4);
150 Int32ToSVBT32(maGlobalSize.Height(), aBT32);
151 nCrc = vcl_get_checksum(nCrc, aBT32, 4);
153 for (auto const& i : maList)
155 BCToBCOA(i->GetChecksum(), aBCOA);
156 nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
159 return nCrc;
162 bool Animation::Start(OutputDevice* pOut, const Point& rDestPt, const Size& rDestSz,
163 tools::Long nExtraData, OutputDevice* pFirstFrameOutDev)
165 bool bRet = false;
167 if (!maList.empty())
169 if ((pOut->GetOutDevType() == OUTDEV_WINDOW) && !mbLoopTerminated
170 && (ANIMATION_TIMEOUT_ON_CLICK != maList[mnPos]->mnWait))
172 bool differs = true;
174 auto itAnimView = std::find_if(
175 maViewList.begin(), maViewList.end(),
176 [pOut, nExtraData](const std::unique_ptr<ImplAnimView>& pAnimView) -> bool {
177 return pAnimView->matches(pOut, nExtraData);
180 if (itAnimView != maViewList.end())
182 if ((*itAnimView)->getOutPos() == rDestPt
183 && (*itAnimView)->getOutSizePix() == pOut->LogicToPixel(rDestSz))
185 (*itAnimView)->repaint();
186 differs = false;
188 else
189 maViewList.erase(itAnimView);
192 if (maViewList.empty())
194 maTimer.Stop();
195 mbIsInAnimation = false;
196 mnPos = 0;
199 if (differs)
200 maViewList.emplace_back(
201 new ImplAnimView(this, pOut, rDestPt, rDestSz, nExtraData, pFirstFrameOutDev));
203 if (!mbIsInAnimation)
205 ImplRestartTimer(maList[mnPos]->mnWait);
206 mbIsInAnimation = true;
209 else
210 Draw(pOut, rDestPt, rDestSz);
212 bRet = true;
215 return bRet;
218 void Animation::Stop(const OutputDevice* pOut, tools::Long nExtraData)
220 maViewList.erase(std::remove_if(maViewList.begin(), maViewList.end(),
221 [=](const std::unique_ptr<ImplAnimView>& pAnimView) -> bool {
222 return pAnimView->matches(pOut, nExtraData);
224 maViewList.end());
226 if (maViewList.empty())
228 maTimer.Stop();
229 mbIsInAnimation = false;
233 void Animation::Draw(OutputDevice* pOut, const Point& rDestPt) const
235 Draw(pOut, rDestPt, pOut->PixelToLogic(maGlobalSize));
238 void Animation::Draw(OutputDevice* pOut, const Point& rDestPt, const Size& rDestSz) const
240 const size_t nCount = maList.size();
242 if (!nCount)
243 return;
245 AnimationBitmap* pObj = maList[std::min(mnPos, nCount - 1)].get();
247 if (pOut->GetConnectMetaFile() || (pOut->GetOutDevType() == OUTDEV_PRINTER))
248 maList[0]->maBitmapEx.Draw(pOut, rDestPt, rDestSz);
249 else if (ANIMATION_TIMEOUT_ON_CLICK == pObj->mnWait)
250 pObj->maBitmapEx.Draw(pOut, rDestPt, rDestSz);
251 else
253 const size_t nOldPos = mnPos;
254 if (mbLoopTerminated)
255 const_cast<Animation*>(this)->mnPos = nCount - 1;
258 ImplAnimView{ const_cast<Animation*>(this), pOut, rDestPt, rDestSz, 0 };
261 const_cast<Animation*>(this)->mnPos = nOldPos;
265 namespace
267 constexpr sal_uLong constMinTimeout = 2;
270 void Animation::ImplRestartTimer(sal_uLong nTimeout)
272 maTimer.SetTimeout(std::max(nTimeout, constMinTimeout) * 10);
273 maTimer.Start();
276 IMPL_LINK_NOARG(Animation, ImplTimeoutHdl, Timer*, void)
278 const size_t nAnimCount = maList.size();
280 if (nAnimCount)
282 bool bGlobalPause = false;
284 if (maNotifyLink.IsSet())
286 std::vector<std::unique_ptr<AInfo>> aAInfoList;
287 // create AInfo-List
288 for (auto const& i : maViewList)
289 aAInfoList.emplace_back(i->createAInfo());
291 maNotifyLink.Call(this);
293 // set view state from AInfo structure
294 for (auto& pAInfo : aAInfoList)
296 ImplAnimView* pView = nullptr;
297 if (!pAInfo->pViewData)
299 pView = new ImplAnimView(this, pAInfo->pOutDev, pAInfo->aStartOrg,
300 pAInfo->aStartSize, pAInfo->nExtraData);
302 maViewList.push_back(std::unique_ptr<ImplAnimView>(pView));
304 else
305 pView = static_cast<ImplAnimView*>(pAInfo->pViewData);
307 pView->pause(pAInfo->bPause);
308 pView->setMarked(true);
311 // delete all unmarked views
312 auto removeStart = std::remove_if(maViewList.begin(), maViewList.end(),
313 [](const auto& pView) { return !pView->isMarked(); });
314 maViewList.erase(removeStart, maViewList.cend());
316 // check if every remaining view is paused
317 bGlobalPause = std::all_of(maViewList.cbegin(), maViewList.cend(),
318 [](const auto& pView) { return pView->isPause(); });
320 // reset marked state
321 std::for_each(maViewList.cbegin(), maViewList.cend(),
322 [](const auto& pView) { pView->setMarked(false); });
325 if (maViewList.empty())
326 Stop();
327 else if (bGlobalPause)
328 ImplRestartTimer(10);
329 else
331 AnimationBitmap* pStepBmp = (++mnPos < maList.size()) ? maList[mnPos].get() : nullptr;
333 if (!pStepBmp)
335 if (mnLoops == 1)
337 Stop();
338 mbLoopTerminated = true;
339 mnPos = nAnimCount - 1;
340 maBitmapEx = maList[mnPos]->maBitmapEx;
341 return;
343 else
345 if (mnLoops)
346 mnLoops--;
348 mnPos = 0;
349 pStepBmp = maList[mnPos].get();
353 // Paint all views.
354 std::for_each(maViewList.cbegin(), maViewList.cend(),
355 [this](const auto& pView) { pView->draw(mnPos); });
357 * If a view is marked, remove the view, because
358 * area of output lies out of display area of window.
359 * Mark state is set from view itself.
361 auto removeStart = std::remove_if(maViewList.begin(), maViewList.end(),
362 [](const auto& pView) { return pView->isMarked(); });
363 maViewList.erase(removeStart, maViewList.cend());
365 // stop or restart timer
366 if (maViewList.empty())
367 Stop();
368 else
369 ImplRestartTimer(pStepBmp->mnWait);
372 else
373 Stop();
376 bool Animation::Insert(const AnimationBitmap& rStepBmp)
378 bool bRet = false;
380 if (!IsInAnimation())
382 tools::Rectangle aGlobalRect(Point(), maGlobalSize);
384 maGlobalSize
385 = aGlobalRect.Union(tools::Rectangle(rStepBmp.maPositionPixel, rStepBmp.maSizePixel))
386 .GetSize();
387 maList.emplace_back(new AnimationBitmap(rStepBmp));
389 // As a start, we make the first BitmapEx the replacement BitmapEx
390 if (maList.size() == 1)
391 maBitmapEx = rStepBmp.maBitmapEx;
393 bRet = true;
396 return bRet;
399 const AnimationBitmap& Animation::Get(sal_uInt16 nAnimation) const
401 SAL_WARN_IF((nAnimation >= maList.size()), "vcl", "No object at this position");
402 return *maList[nAnimation];
405 void Animation::Replace(const AnimationBitmap& rNewAnimationBitmap, sal_uInt16 nAnimation)
407 SAL_WARN_IF((nAnimation >= maList.size()), "vcl", "No object at this position");
409 maList[nAnimation].reset(new AnimationBitmap(rNewAnimationBitmap));
411 // If we insert at first position we also need to
412 // update the replacement BitmapEx
413 if ((!nAnimation && (!mbLoopTerminated || (maList.size() == 1)))
414 || ((nAnimation == maList.size() - 1) && mbLoopTerminated))
416 maBitmapEx = rNewAnimationBitmap.maBitmapEx;
420 void Animation::SetLoopCount(const sal_uInt32 nLoopCount)
422 mnLoopCount = nLoopCount;
423 ResetLoopCount();
426 void Animation::ResetLoopCount()
428 mnLoops = mnLoopCount;
429 mbLoopTerminated = false;
432 void Animation::Convert(BmpConversion eConversion)
434 SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
436 bool bRet;
438 if (!IsInAnimation() && !maList.empty())
440 bRet = true;
442 for (size_t i = 0, n = maList.size(); (i < n) && bRet; ++i)
443 bRet = maList[i]->maBitmapEx.Convert(eConversion);
445 maBitmapEx.Convert(eConversion);
449 bool Animation::ReduceColors(sal_uInt16 nNewColorCount)
451 SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
453 bool bRet;
455 if (!IsInAnimation() && !maList.empty())
457 bRet = true;
459 for (size_t i = 0, n = maList.size(); (i < n) && bRet; ++i)
461 bRet = BitmapFilter::Filter(maList[i]->maBitmapEx,
462 BitmapColorQuantizationFilter(nNewColorCount));
465 BitmapFilter::Filter(maBitmapEx, BitmapColorQuantizationFilter(nNewColorCount));
467 else
469 bRet = false;
472 return bRet;
475 bool Animation::Invert()
477 SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
479 bool bRet;
481 if (!IsInAnimation() && !maList.empty())
483 bRet = true;
485 for (size_t i = 0, n = maList.size(); (i < n) && bRet; ++i)
486 bRet = maList[i]->maBitmapEx.Invert();
488 maBitmapEx.Invert();
490 else
491 bRet = false;
493 return bRet;
496 void Animation::Mirror(BmpMirrorFlags nMirrorFlags)
498 SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
500 bool bRet;
502 if (IsInAnimation() || maList.empty())
503 return;
505 bRet = true;
507 if (nMirrorFlags == BmpMirrorFlags::NONE)
508 return;
510 for (size_t i = 0, n = maList.size(); (i < n) && bRet; ++i)
512 AnimationBitmap* pStepBmp = maList[i].get();
513 bRet = pStepBmp->maBitmapEx.Mirror(nMirrorFlags);
514 if (bRet)
516 if (nMirrorFlags & BmpMirrorFlags::Horizontal)
517 pStepBmp->maPositionPixel.setX(maGlobalSize.Width() - pStepBmp->maPositionPixel.X()
518 - pStepBmp->maSizePixel.Width());
520 if (nMirrorFlags & BmpMirrorFlags::Vertical)
521 pStepBmp->maPositionPixel.setY(maGlobalSize.Height() - pStepBmp->maPositionPixel.Y()
522 - pStepBmp->maSizePixel.Height());
526 maBitmapEx.Mirror(nMirrorFlags);
529 void Animation::Adjust(short nLuminancePercent, short nContrastPercent, short nChannelRPercent,
530 short nChannelGPercent, short nChannelBPercent, double fGamma, bool bInvert)
532 SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
534 bool bRet;
536 if (IsInAnimation() || maList.empty())
537 return;
539 bRet = true;
541 for (size_t i = 0, n = maList.size(); (i < n) && bRet; ++i)
543 bRet = maList[i]->maBitmapEx.Adjust(nLuminancePercent, nContrastPercent, nChannelRPercent,
544 nChannelGPercent, nChannelBPercent, fGamma, bInvert);
547 maBitmapEx.Adjust(nLuminancePercent, nContrastPercent, nChannelRPercent, nChannelGPercent,
548 nChannelBPercent, fGamma, bInvert);
551 SvStream& WriteAnimation(SvStream& rOStm, const Animation& rAnimation)
553 const sal_uInt16 nCount = rAnimation.Count();
555 if (nCount)
557 const sal_uInt32 nDummy32 = 0;
559 // If no BitmapEx was set we write the first Bitmap of
560 // the Animation
561 if (!rAnimation.GetBitmapEx().GetBitmap())
562 WriteDIBBitmapEx(rAnimation.Get(0).maBitmapEx, rOStm);
563 else
564 WriteDIBBitmapEx(rAnimation.GetBitmapEx(), rOStm);
566 // Write identifier ( SDANIMA1 )
567 rOStm.WriteUInt32(0x5344414e).WriteUInt32(0x494d4931);
569 for (sal_uInt16 i = 0; i < nCount; i++)
571 const AnimationBitmap& rAnimationBitmap = rAnimation.Get(i);
572 const sal_uInt16 nRest = nCount - i - 1;
574 // Write AnimationBitmap
575 WriteDIBBitmapEx(rAnimationBitmap.maBitmapEx, rOStm);
576 tools::GenericTypeSerializer aSerializer(rOStm);
577 aSerializer.writePoint(rAnimationBitmap.maPositionPixel);
578 aSerializer.writeSize(rAnimationBitmap.maSizePixel);
579 aSerializer.writeSize(rAnimation.maGlobalSize);
580 rOStm.WriteUInt16((ANIMATION_TIMEOUT_ON_CLICK == rAnimationBitmap.mnWait)
581 ? 65535
582 : rAnimationBitmap.mnWait);
583 rOStm.WriteUInt16(static_cast<sal_uInt16>(rAnimationBitmap.meDisposal));
584 rOStm.WriteBool(rAnimationBitmap.mbUserInput);
585 rOStm.WriteUInt32(rAnimation.mnLoopCount);
586 rOStm.WriteUInt32(nDummy32); // Unused
587 rOStm.WriteUInt32(nDummy32); // Unused
588 rOStm.WriteUInt32(nDummy32); // Unused
589 write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm, OString()); // dummy
590 rOStm.WriteUInt16(nRest); // Count of remaining structures
594 return rOStm;
597 SvStream& ReadAnimation(SvStream& rIStm, Animation& rAnimation)
599 sal_uLong nStmPos;
600 sal_uInt32 nAnimMagic1, nAnimMagic2;
601 SvStreamEndian nOldFormat = rIStm.GetEndian();
602 bool bReadAnimations = false;
604 rIStm.SetEndian(SvStreamEndian::LITTLE);
605 nStmPos = rIStm.Tell();
606 rIStm.ReadUInt32(nAnimMagic1).ReadUInt32(nAnimMagic2);
608 rAnimation.Clear();
610 // If the BitmapEx at the beginning have already been read (by Graphic)
611 // we can start reading the AnimationBitmaps right away
612 if ((nAnimMagic1 == 0x5344414e) && (nAnimMagic2 == 0x494d4931) && !rIStm.GetError())
613 bReadAnimations = true;
614 // Else, we try reading the Bitmap(-Ex)
615 else
617 rIStm.Seek(nStmPos);
618 ReadDIBBitmapEx(rAnimation.maBitmapEx, rIStm);
619 nStmPos = rIStm.Tell();
620 rIStm.ReadUInt32(nAnimMagic1).ReadUInt32(nAnimMagic2);
622 if ((nAnimMagic1 == 0x5344414e) && (nAnimMagic2 == 0x494d4931) && !rIStm.GetError())
623 bReadAnimations = true;
624 else
625 rIStm.Seek(nStmPos);
628 // Read AnimationBitmaps
629 if (bReadAnimations)
631 AnimationBitmap aAnimationBitmap;
632 sal_uInt32 nTmp32;
633 sal_uInt16 nTmp16;
634 bool cTmp;
638 ReadDIBBitmapEx(aAnimationBitmap.maBitmapEx, rIStm);
639 tools::GenericTypeSerializer aSerializer(rIStm);
640 aSerializer.readPoint(aAnimationBitmap.maPositionPixel);
641 aSerializer.readSize(aAnimationBitmap.maSizePixel);
642 aSerializer.readSize(rAnimation.maGlobalSize);
643 rIStm.ReadUInt16(nTmp16);
644 aAnimationBitmap.mnWait = ((65535 == nTmp16) ? ANIMATION_TIMEOUT_ON_CLICK : nTmp16);
645 rIStm.ReadUInt16(nTmp16);
646 aAnimationBitmap.meDisposal = static_cast<Disposal>(nTmp16);
647 rIStm.ReadCharAsBool(cTmp);
648 aAnimationBitmap.mbUserInput = cTmp;
649 rIStm.ReadUInt32(rAnimation.mnLoopCount);
650 rIStm.ReadUInt32(nTmp32); // Unused
651 rIStm.ReadUInt32(nTmp32); // Unused
652 rIStm.ReadUInt32(nTmp32); // Unused
653 read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm); // Unused
654 rIStm.ReadUInt16(nTmp16); // The rest to read
656 rAnimation.Insert(aAnimationBitmap);
657 } while (nTmp16 && !rIStm.GetError());
659 rAnimation.ResetLoopCount();
662 rIStm.SetEndian(nOldFormat);
664 return rIStm;
667 AInfo::AInfo()
668 : pOutDev(nullptr)
669 , pViewData(nullptr)
670 , nExtraData(0)
671 , bPause(false)
675 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */