/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: */ |