Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/animate/Animation.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
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/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
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 .
18
 */
19
20
#include <algorithm>
21
#include <sal/config.h>
22
23
#include <rtl/crc.h>
24
#include <tools/stream.hxx>
25
#include <tools/GenericTypeSerializer.hxx>
26
#include <sal/log.hxx>
27
28
#include <vcl/animate/Animation.hxx>
29
#include <vcl/bitmap/BitmapColorQuantizationFilter.hxx>
30
#include <vcl/dibtools.hxx>
31
#include <vcl/outdev.hxx>
32
33
#include <animate/AnimationRenderer.hxx>
34
35
sal_uLong Animation::gAnimationRendererCount = 0;
36
37
Animation::Animation()
38
20.1k
    : maTimer("vcl::Animation")
39
20.1k
    , mnLoopCount(0)
40
20.1k
    , mnLoops(0)
41
20.1k
    , mnFrameIndex(0)
42
20.1k
    , mbIsInAnimation(false)
43
20.1k
    , mbLoopTerminated(false)
44
20.1k
{
45
20.1k
    maTimer.SetInvokeHandler(LINK(this, Animation, ImplTimeoutHdl));
46
20.1k
}
47
48
Animation::Animation(const Animation& rAnimation)
49
2.39k
    : maBitmap(rAnimation.maBitmap)
50
2.39k
    , maTimer("vcl::Animation")
51
2.39k
    , maGlobalSize(rAnimation.maGlobalSize)
52
2.39k
    , mnLoopCount(rAnimation.mnLoopCount)
53
2.39k
    , mnFrameIndex(rAnimation.mnFrameIndex)
54
2.39k
    , mbIsInAnimation(false)
55
2.39k
    , mbLoopTerminated(rAnimation.mbLoopTerminated)
56
2.39k
{
57
2.39k
    for (auto const& rFrame : rAnimation.maFrames)
58
50.1k
        maFrames.emplace_back(new AnimationFrame(*rFrame));
59
60
2.39k
    maTimer.SetInvokeHandler(LINK(this, Animation, ImplTimeoutHdl));
61
2.39k
    mnLoops = mbLoopTerminated ? 0 : mnLoopCount;
62
2.39k
}
63
64
Animation::~Animation()
65
22.5k
{
66
22.5k
    if (mbIsInAnimation)
67
0
        Stop();
68
22.5k
}
69
70
Animation& Animation::operator=(const Animation& rAnimation)
71
0
{
72
0
    if (this != &rAnimation)
73
0
    {
74
0
        Clear();
75
76
0
        for (auto const& i : rAnimation.maFrames)
77
0
            maFrames.emplace_back(new AnimationFrame(*i));
78
79
0
        maGlobalSize = rAnimation.maGlobalSize;
80
0
        maBitmap = rAnimation.maBitmap;
81
0
        mnLoopCount = rAnimation.mnLoopCount;
82
0
        mnFrameIndex = rAnimation.mnFrameIndex;
83
0
        mbLoopTerminated = rAnimation.mbLoopTerminated;
84
0
        mnLoops = mbLoopTerminated ? 0 : mnLoopCount;
85
0
    }
86
0
    return *this;
87
0
}
88
89
bool Animation::operator==(const Animation& rAnimation) const
90
0
{
91
0
    return maFrames.size() == rAnimation.maFrames.size() && maBitmap == rAnimation.maBitmap
92
0
           && maGlobalSize == rAnimation.maGlobalSize
93
0
           && std::equal(maFrames.begin(), maFrames.end(), rAnimation.maFrames.begin(),
94
0
                         [](const std::unique_ptr<AnimationFrame>& pAnim1,
95
0
                            const std::unique_ptr<AnimationFrame>& pAnim2) -> bool {
96
0
                             return *pAnim1 == *pAnim2;
97
0
                         });
98
0
}
99
100
void Animation::Clear()
101
0
{
102
0
    maTimer.Stop();
103
0
    mbIsInAnimation = false;
104
0
    maGlobalSize = Size();
105
0
    maBitmap.SetEmpty();
106
0
    maFrames.clear();
107
0
    maRenderers.clear();
108
0
}
109
110
bool Animation::IsTransparent() const
111
0
{
112
0
    tools::Rectangle aRect{ Point(), maGlobalSize };
113
114
    // If some small bitmap needs to be replaced by the background,
115
    // we need to be transparent, in order to be displayed correctly
116
    // as the application (?) does not invalidate on non-transparent
117
    // graphics due to performance reasons.
118
119
0
    return maBitmap.HasAlpha()
120
0
           || std::any_of(maFrames.begin(), maFrames.end(),
121
0
                          [&aRect](const std::unique_ptr<AnimationFrame>& pAnim) -> bool {
122
0
                              return pAnim->meDisposal == Disposal::Back
123
0
                                     && tools::Rectangle{ pAnim->maPositionPixel,
124
0
                                                          pAnim->maSizePixel }
125
0
                                            != aRect;
126
0
                          });
127
0
}
128
129
sal_uLong Animation::GetSizeBytes() const
130
904
{
131
904
    return std::accumulate(maFrames.begin(), maFrames.end(), GetBitmap().GetSizeBytes(),
132
2.26k
                           [](sal_Int64 nSize, const std::unique_ptr<AnimationFrame>& pFrame) {
133
2.26k
                               return nSize + pFrame->maBitmap.GetSizeBytes();
134
2.26k
                           });
135
904
}
136
137
BitmapChecksum Animation::GetChecksum() const
138
0
{
139
0
    SVBT32 aBT32;
140
0
    BitmapChecksumOctetArray aBCOA;
141
0
    BitmapChecksum nCrc = GetBitmap().GetChecksum();
142
143
0
    UInt32ToSVBT32(maFrames.size(), aBT32);
144
0
    nCrc = rtl_crc32(nCrc, aBT32, 4);
145
146
0
    Int32ToSVBT32(maGlobalSize.Width(), aBT32);
147
0
    nCrc = rtl_crc32(nCrc, aBT32, 4);
148
149
0
    Int32ToSVBT32(maGlobalSize.Height(), aBT32);
150
0
    nCrc = rtl_crc32(nCrc, aBT32, 4);
151
152
0
    for (auto const& i : maFrames)
153
0
    {
154
0
        BCToBCOA(i->GetChecksum(), aBCOA);
155
0
        nCrc = rtl_crc32(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
156
0
    }
157
158
0
    return nCrc;
159
0
}
160
161
bool Animation::Start(OutputDevice& rOut, const Point& rDestPt, const Size& rDestSz,
162
                      tools::Long nRendererId, OutputDevice* pFirstFrameOutDev)
163
0
{
164
0
    if (maFrames.empty())
165
0
        return false;
166
167
0
    if (rOut.CanAnimate() && !mbLoopTerminated
168
0
        && (ANIMATION_TIMEOUT_ON_CLICK != maFrames[mnFrameIndex]->mnWait))
169
0
    {
170
0
        bool differs = true;
171
172
0
        auto itAnimView = std::find_if(
173
0
            maRenderers.begin(), maRenderers.end(),
174
0
            [&rOut, nRendererId](const std::unique_ptr<AnimationRenderer>& pRenderer) -> bool {
175
0
                return pRenderer->matches(&rOut, nRendererId);
176
0
            });
177
178
0
        if (itAnimView != maRenderers.end())
179
0
        {
180
0
            if ((*itAnimView)->getOriginPosition() == rDestPt
181
0
                && (*itAnimView)->getOutSizePix() == rOut.LogicToPixel(rDestSz))
182
0
            {
183
0
                (*itAnimView)->repaint();
184
0
                differs = false;
185
0
            }
186
0
            else
187
0
            {
188
0
                maRenderers.erase(itAnimView);
189
0
            }
190
0
        }
191
192
0
        if (maRenderers.empty())
193
0
        {
194
0
            maTimer.Stop();
195
0
            mbIsInAnimation = false;
196
0
            mnFrameIndex = 0;
197
0
        }
198
199
0
        if (differs)
200
0
            maRenderers.emplace_back(new AnimationRenderer(this, &rOut, rDestPt, rDestSz,
201
0
                                                           nRendererId, pFirstFrameOutDev));
202
203
0
        if (!mbIsInAnimation)
204
0
        {
205
0
            ImplRestartTimer(maFrames[mnFrameIndex]->mnWait);
206
0
            mbIsInAnimation = true;
207
0
        }
208
0
    }
209
0
    else
210
0
    {
211
0
        Draw(rOut, rDestPt, rDestSz);
212
0
    }
213
214
0
    return true;
215
0
}
216
217
void Animation::Stop(const OutputDevice* pOut, tools::Long nRendererId)
218
0
{
219
0
    std::erase_if(maRenderers, [=](const std::unique_ptr<AnimationRenderer>& pRenderer) -> bool {
220
0
        return pRenderer->matches(pOut, nRendererId);
221
0
    });
222
223
0
    if (maRenderers.empty())
224
0
    {
225
0
        maTimer.Stop();
226
0
        mbIsInAnimation = false;
227
0
    }
228
0
}
229
230
void Animation::Draw(OutputDevice& rOut, const Point& rDestPt) const
231
0
{
232
0
    Draw(rOut, rDestPt, rOut.PixelToLogic(maGlobalSize));
233
0
}
234
235
void Animation::Draw(OutputDevice& rOut, const Point& rDestPt, const Size& rDestSz) const
236
0
{
237
0
    const size_t nCount = maFrames.size();
238
239
0
    if (!nCount)
240
0
        return;
241
242
0
    AnimationFrame* pObj = maFrames[std::min(mnFrameIndex, nCount - 1)].get();
243
244
0
    if (rOut.GetConnectMetaFile() || (rOut.GetOutDevType() == OUTDEV_PRINTER))
245
0
    {
246
0
        maFrames[0]->maBitmap.Draw(&rOut, rDestPt, rDestSz);
247
0
    }
248
0
    else if (ANIMATION_TIMEOUT_ON_CLICK == pObj->mnWait)
249
0
    {
250
0
        pObj->maBitmap.Draw(&rOut, rDestPt, rDestSz);
251
0
    }
252
0
    else
253
0
    {
254
0
        const size_t nOldPos = mnFrameIndex;
255
0
        if (mbLoopTerminated)
256
0
            const_cast<Animation*>(this)->mnFrameIndex = nCount - 1;
257
258
0
        {
259
0
            AnimationRenderer{ const_cast<Animation*>(this), &rOut, rDestPt, rDestSz, 0 };
260
0
        }
261
262
0
        const_cast<Animation*>(this)->mnFrameIndex = nOldPos;
263
0
    }
264
0
}
265
266
namespace
267
{
268
constexpr sal_uLong constMinTimeout = 2;
269
}
270
271
void Animation::ImplRestartTimer(sal_uLong nTimeout)
272
0
{
273
0
    maTimer.SetTimeout(std::max(nTimeout, constMinTimeout) * 10);
274
0
    maTimer.Start();
275
0
}
276
277
std::vector<std::unique_ptr<AnimationData>> Animation::CreateAnimationDataItems()
278
0
{
279
0
    std::vector<std::unique_ptr<AnimationData>> aDataItems;
280
281
0
    for (auto const& rItem : maRenderers)
282
0
    {
283
0
        aDataItems.emplace_back(rItem->createAnimationData());
284
0
    }
285
286
0
    return aDataItems;
287
0
}
288
289
void Animation::PopulateRenderers()
290
0
{
291
0
    for (auto& pDataItem : CreateAnimationDataItems())
292
0
    {
293
0
        AnimationRenderer* pRenderer = nullptr;
294
0
        if (!pDataItem->mpRendererData)
295
0
        {
296
0
            pRenderer = new AnimationRenderer(this, pDataItem->mpRenderContext,
297
0
                                              pDataItem->maOriginStartPt, pDataItem->maStartSize,
298
0
                                              pDataItem->mnRendererId);
299
300
0
            maRenderers.push_back(std::unique_ptr<AnimationRenderer>(pRenderer));
301
0
        }
302
0
        else
303
0
        {
304
0
            pRenderer = pDataItem->mpRendererData;
305
0
        }
306
307
0
        pRenderer->pause(pDataItem->mbIsPaused);
308
0
        pRenderer->setMarked(true);
309
0
    }
310
0
}
311
312
void Animation::RenderNextFrameInAllRenderers()
313
0
{
314
0
    AnimationFrame* pCurrentFrameBmp
315
0
        = (++mnFrameIndex < maFrames.size()) ? maFrames[mnFrameIndex].get() : nullptr;
316
317
0
    if (!pCurrentFrameBmp)
318
0
    {
319
0
        if (mnLoops == 1)
320
0
        {
321
0
            Stop();
322
0
            mbLoopTerminated = true;
323
0
            mnFrameIndex = maFrames.size() - 1;
324
0
            maBitmap = maFrames[mnFrameIndex]->maBitmap;
325
0
            return;
326
0
        }
327
0
        else
328
0
        {
329
0
            if (mnLoops)
330
0
                mnLoops--;
331
332
0
            mnFrameIndex = 0;
333
0
            pCurrentFrameBmp = maFrames[mnFrameIndex].get();
334
0
        }
335
0
    }
336
337
    // Paint all views.
338
0
    std::for_each(maRenderers.cbegin(), maRenderers.cend(),
339
0
                  [this](const auto& pRenderer) { pRenderer->draw(mnFrameIndex); });
340
    /*
341
     * If a view is marked, remove the view, because
342
     * area of output lies out of display area of window.
343
     * Mark state is set from view itself.
344
     */
345
0
    std::erase_if(maRenderers, [](const auto& pRenderer) { return pRenderer->isMarked(); });
346
347
    // stop or restart timer
348
0
    if (maRenderers.empty())
349
0
        Stop();
350
0
    else
351
0
        ImplRestartTimer(pCurrentFrameBmp->mnWait);
352
0
}
353
354
void Animation::PruneMarkedRenderers()
355
0
{
356
    // delete all unmarked views
357
0
    std::erase_if(maRenderers, [](const auto& pRenderer) { return !pRenderer->isMarked(); });
358
359
    // reset marked state
360
0
    std::for_each(maRenderers.cbegin(), maRenderers.cend(),
361
0
                  [](const auto& pRenderer) { pRenderer->setMarked(false); });
362
0
}
363
364
bool Animation::IsAnyRendererActive()
365
0
{
366
0
    return std::any_of(maRenderers.cbegin(), maRenderers.cend(),
367
0
                       [](const auto& pRenderer) { return !pRenderer->isPaused(); });
368
0
}
369
370
IMPL_LINK_NOARG(Animation, ImplTimeoutHdl, Timer*, void)
371
0
{
372
0
    const size_t nAnimCount = maFrames.size();
373
374
0
    if (!nAnimCount)
375
0
    {
376
0
        Stop();
377
0
        return;
378
0
    }
379
380
0
    bool bIsAnyRendererActive = true;
381
382
0
    if (maNotifyLink.IsSet())
383
0
    {
384
0
        maNotifyLink.Call(this);
385
0
        PopulateRenderers();
386
0
        PruneMarkedRenderers();
387
0
        bIsAnyRendererActive = IsAnyRendererActive();
388
0
    }
389
390
0
    if (maRenderers.empty())
391
0
        Stop();
392
0
    else if (!bIsAnyRendererActive)
393
0
        ImplRestartTimer(10);
394
0
    else
395
0
        RenderNextFrameInAllRenderers();
396
0
}
397
398
bool Animation::Insert(const AnimationFrame& rStepBmp)
399
59.5k
{
400
59.5k
    if (IsInAnimation())
401
0
        return false;
402
403
59.5k
    tools::Rectangle aGlobalRect(Point(), maGlobalSize);
404
405
59.5k
    maGlobalSize
406
59.5k
        = aGlobalRect.Union(tools::Rectangle(rStepBmp.maPositionPixel, rStepBmp.maSizePixel))
407
59.5k
              .GetSize();
408
59.5k
    maFrames.emplace_back(new AnimationFrame(rStepBmp));
409
410
    // As a start, we make the first Bitmap the replacement Bitmap
411
59.5k
    if (maFrames.size() == 1)
412
6.76k
        maBitmap = rStepBmp.maBitmap;
413
414
59.5k
    return true;
415
59.5k
}
416
417
const AnimationFrame& Animation::Get(sal_uInt16 nAnimation) const
418
487
{
419
487
    SAL_WARN_IF((nAnimation >= maFrames.size()), "vcl", "No object at this position");
420
487
    return *maFrames[nAnimation];
421
487
}
422
423
void Animation::Replace(const AnimationFrame& rNewAnimationFrame, sal_uInt16 nAnimation)
424
0
{
425
0
    SAL_WARN_IF((nAnimation >= maFrames.size()), "vcl", "No object at this position");
426
427
0
    maFrames[nAnimation].reset(new AnimationFrame(rNewAnimationFrame));
428
429
    // If we insert at first position we also need to
430
    // update the replacement Bitmap
431
0
    if ((!nAnimation && (!mbLoopTerminated || (maFrames.size() == 1)))
432
0
        || ((nAnimation == maFrames.size() - 1) && mbLoopTerminated))
433
0
    {
434
0
        maBitmap = rNewAnimationFrame.maBitmap;
435
0
    }
436
0
}
437
438
void Animation::SetLoopCount(const sal_uInt32 nLoopCount)
439
2.56k
{
440
2.56k
    mnLoopCount = nLoopCount;
441
2.56k
    ResetLoopCount();
442
2.56k
}
443
444
void Animation::ResetLoopCount()
445
2.56k
{
446
2.56k
    mnLoops = mnLoopCount;
447
2.56k
    mbLoopTerminated = false;
448
2.56k
}
449
450
void Animation::Convert(BmpConversion eConversion)
451
0
{
452
0
    SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
453
454
0
    if (IsInAnimation() || maFrames.empty())
455
0
        return;
456
457
0
    bool bRet = true;
458
459
0
    for (size_t i = 0, n = maFrames.size(); (i < n) && bRet; ++i)
460
0
    {
461
0
        bRet = maFrames[i]->maBitmap.Convert(eConversion);
462
0
    }
463
464
0
    maBitmap.Convert(eConversion);
465
0
}
466
467
bool Animation::ReduceColors(sal_uInt16 nNewColorCount)
468
0
{
469
0
    SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
470
471
0
    if (IsInAnimation() || maFrames.empty())
472
0
        return false;
473
474
0
    bool bRet = true;
475
476
0
    for (size_t i = 0, n = maFrames.size(); (i < n) && bRet; ++i)
477
0
    {
478
0
        bRet = BitmapFilter::Filter(maFrames[i]->maBitmap,
479
0
                                    BitmapColorQuantizationFilter(nNewColorCount));
480
0
    }
481
482
0
    BitmapFilter::Filter(maBitmap, BitmapColorQuantizationFilter(nNewColorCount));
483
484
0
    return bRet;
485
0
}
486
487
bool Animation::Invert()
488
0
{
489
0
    SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
490
491
0
    if (IsInAnimation() || maFrames.empty())
492
0
        return false;
493
494
0
    maBitmap.Invert();
495
496
0
    for (auto& pFrame : maFrames)
497
0
    {
498
0
        if (!pFrame->maBitmap.Invert())
499
0
            return false;
500
0
    }
501
502
0
    return true;
503
0
}
504
505
void Animation::Mirror(BmpMirrorFlags nMirrorFlags)
506
0
{
507
0
    SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
508
509
0
    if (IsInAnimation() || maFrames.empty())
510
0
        return;
511
512
0
    if (nMirrorFlags == BmpMirrorFlags::NONE)
513
0
        return;
514
515
0
    bool bRet = true;
516
517
0
    for (size_t i = 0, n = maFrames.size(); (i < n) && bRet; ++i)
518
0
    {
519
0
        AnimationFrame* pCurrentFrameBmp = maFrames[i].get();
520
0
        bRet = pCurrentFrameBmp->maBitmap.Mirror(nMirrorFlags);
521
0
        if (bRet)
522
0
        {
523
0
            if (nMirrorFlags & BmpMirrorFlags::Horizontal)
524
0
                pCurrentFrameBmp->maPositionPixel.setX(maGlobalSize.Width()
525
0
                                                       - pCurrentFrameBmp->maPositionPixel.X()
526
0
                                                       - pCurrentFrameBmp->maSizePixel.Width());
527
528
0
            if (nMirrorFlags & BmpMirrorFlags::Vertical)
529
0
                pCurrentFrameBmp->maPositionPixel.setY(maGlobalSize.Height()
530
0
                                                       - pCurrentFrameBmp->maPositionPixel.Y()
531
0
                                                       - pCurrentFrameBmp->maSizePixel.Height());
532
0
        }
533
0
    }
534
535
0
    maBitmap.Mirror(nMirrorFlags);
536
0
}
537
538
void Animation::Adjust(short nLuminancePercent, short nContrastPercent, short nChannelRPercent,
539
                       short nChannelGPercent, short nChannelBPercent, double fGamma, bool bInvert)
540
0
{
541
0
    SAL_WARN_IF(IsInAnimation(), "vcl", "Animation modified while it is animated");
542
543
0
    if (IsInAnimation() || maFrames.empty())
544
0
        return;
545
546
0
    bool bRet = true;
547
548
0
    for (size_t i = 0, n = maFrames.size(); (i < n) && bRet; ++i)
549
0
    {
550
0
        bRet = maFrames[i]->maBitmap.Adjust(nLuminancePercent, nContrastPercent, nChannelRPercent,
551
0
                                            nChannelGPercent, nChannelBPercent, fGamma, bInvert);
552
0
    }
553
554
0
    maBitmap.Adjust(nLuminancePercent, nContrastPercent, nChannelRPercent, nChannelGPercent,
555
0
                    nChannelBPercent, fGamma, bInvert);
556
0
}
557
558
SvStream& WriteAnimation(SvStream& rOStm, const Animation& rAnimation)
559
0
{
560
0
    const sal_uInt16 nCount = rAnimation.Count();
561
562
0
    if (!nCount)
563
0
        return rOStm;
564
565
0
    const sal_uInt32 nDummy32 = 0;
566
567
    // If no Bitmap was set we write the first Bitmap of
568
    // the Animation
569
0
    if (rAnimation.GetBitmap().IsEmpty())
570
0
        WriteDIBBitmapEx(rAnimation.Get(0).maBitmap, rOStm);
571
0
    else
572
0
        WriteDIBBitmapEx(rAnimation.GetBitmap(), rOStm);
573
574
    // Write identifier ( SDANIMA1 )
575
0
    rOStm.WriteUInt32(0x5344414e).WriteUInt32(0x494d4931);
576
577
0
    for (sal_uInt16 i = 0; i < nCount; i++)
578
0
    {
579
0
        const AnimationFrame& rAnimationFrame = rAnimation.Get(i);
580
0
        const sal_uInt16 nRest = nCount - i - 1;
581
582
        // Write AnimationFrame
583
0
        WriteDIBBitmapEx(rAnimationFrame.maBitmap, rOStm);
584
0
        tools::GenericTypeSerializer aSerializer(rOStm);
585
0
        aSerializer.writePoint(rAnimationFrame.maPositionPixel);
586
0
        aSerializer.writeSize(rAnimationFrame.maSizePixel);
587
0
        aSerializer.writeSize(rAnimation.maGlobalSize);
588
0
        rOStm.WriteUInt16((ANIMATION_TIMEOUT_ON_CLICK == rAnimationFrame.mnWait)
589
0
                              ? 65535
590
0
                              : rAnimationFrame.mnWait);
591
0
        rOStm.WriteUInt16(static_cast<sal_uInt16>(rAnimationFrame.meDisposal));
592
0
        rOStm.WriteBool(rAnimationFrame.mbUserInput);
593
0
        rOStm.WriteUInt32(rAnimation.mnLoopCount);
594
0
        rOStm.WriteUInt32(nDummy32); // Unused
595
0
        rOStm.WriteUInt32(nDummy32); // Unused
596
0
        rOStm.WriteUInt32(nDummy32); // Unused
597
0
        write_uInt16_lenPrefixed_uInt8s_FromOString(rOStm, ""); // dummy
598
0
        rOStm.WriteUInt16(nRest); // Count of remaining structures
599
0
    }
600
601
0
    return rOStm;
602
0
}
603
604
SvStream& ReadAnimation(SvStream& rIStm, Animation& rAnimation)
605
0
{
606
0
    sal_uLong nStmPos;
607
0
    sal_uInt32 nAnimMagic1, nAnimMagic2;
608
0
    SvStreamEndian nOldFormat = rIStm.GetEndian();
609
0
    bool bReadAnimations = false;
610
611
0
    rIStm.SetEndian(SvStreamEndian::LITTLE);
612
0
    nStmPos = rIStm.Tell();
613
0
    rIStm.ReadUInt32(nAnimMagic1).ReadUInt32(nAnimMagic2);
614
615
0
    rAnimation.Clear();
616
617
    // If the Bitmap at the beginning have already been read (by Graphic)
618
    // we can start reading the AnimationFrames right away
619
0
    if ((nAnimMagic1 == 0x5344414e) && (nAnimMagic2 == 0x494d4931) && !rIStm.GetError())
620
0
    {
621
0
        bReadAnimations = true;
622
0
    }
623
    // Else, we try reading the Bitmap(-Ex)
624
0
    else
625
0
    {
626
0
        rIStm.Seek(nStmPos);
627
0
        ReadDIBBitmapEx(rAnimation.maBitmap, rIStm);
628
0
        nStmPos = rIStm.Tell();
629
0
        rIStm.ReadUInt32(nAnimMagic1).ReadUInt32(nAnimMagic2);
630
631
0
        if ((nAnimMagic1 == 0x5344414e) && (nAnimMagic2 == 0x494d4931) && !rIStm.GetError())
632
0
            bReadAnimations = true;
633
0
        else
634
0
            rIStm.Seek(nStmPos);
635
0
    }
636
637
    // Read AnimationFrames
638
0
    if (bReadAnimations)
639
0
    {
640
0
        AnimationFrame aAnimationFrame;
641
0
        sal_uInt32 nTmp32;
642
0
        sal_uInt16 nTmp16;
643
0
        bool cTmp;
644
645
0
        do
646
0
        {
647
0
            ReadDIBBitmapEx(aAnimationFrame.maBitmap, rIStm);
648
0
            tools::GenericTypeSerializer aSerializer(rIStm);
649
0
            aSerializer.readPoint(aAnimationFrame.maPositionPixel);
650
0
            aSerializer.readSize(aAnimationFrame.maSizePixel);
651
0
            aSerializer.readSize(rAnimation.maGlobalSize);
652
0
            rIStm.ReadUInt16(nTmp16);
653
0
            aAnimationFrame.mnWait = ((65535 == nTmp16) ? ANIMATION_TIMEOUT_ON_CLICK : nTmp16);
654
0
            rIStm.ReadUInt16(nTmp16);
655
0
            aAnimationFrame.meDisposal = static_cast<Disposal>(nTmp16);
656
0
            rIStm.ReadCharAsBool(cTmp);
657
0
            aAnimationFrame.mbUserInput = cTmp;
658
0
            rIStm.ReadUInt32(rAnimation.mnLoopCount);
659
0
            rIStm.ReadUInt32(nTmp32); // Unused
660
0
            rIStm.ReadUInt32(nTmp32); // Unused
661
0
            rIStm.ReadUInt32(nTmp32); // Unused
662
0
            read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm); // Unused
663
0
            rIStm.ReadUInt16(nTmp16); // The rest to read
664
665
0
            rAnimation.Insert(aAnimationFrame);
666
0
        } while (nTmp16 && !rIStm.GetError());
667
668
0
        rAnimation.ResetLoopCount();
669
0
    }
670
671
0
    rIStm.SetEndian(nOldFormat);
672
673
0
    return rIStm;
674
0
}
675
676
AnimationData::AnimationData()
677
0
    : mpRenderContext(nullptr)
678
0
    , mpRendererData(nullptr)
679
0
    , mnRendererId(0)
680
0
    , mbIsPaused(false)
681
0
{
682
0
}
683
684
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */