Merge pull request #26166 from ksooo/improve-plugin-ctx-menus
[xbmc.git] / xbmc / pictures / SlideShowPicture.cpp
blob64dbbea0d055e5a36cc178a3be868fdca917f9c9
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "SlideShowPicture.h"
11 #include "ServiceBroker.h"
12 #include "guilib/Texture.h"
13 #include "settings/AdvancedSettings.h"
14 #include "settings/Settings.h"
15 #include "settings/SettingsComponent.h"
16 #include "windowing/GraphicContext.h"
17 #include "windowing/WinSystem.h"
19 #include <cassert>
20 #include <mutex>
22 #ifndef _USE_MATH_DEFINES
23 #define _USE_MATH_DEFINES
24 #endif
25 #include <cstddef>
26 #include <math.h>
28 #define IMMEDIATE_TRANSITION_TIME 20
30 #define PICTURE_MOVE_AMOUNT 0.02f
31 #define PICTURE_MOVE_AMOUNT_ANALOG 0.01f
32 #define PICTURE_VIEW_BOX_COLOR 0xffffff00 // YELLOW
33 #define PICTURE_VIEW_BOX_BACKGROUND 0xff000000 // BLACK
35 #define FPS 25
37 static float zoomamount[10] = { 1.0f, 1.2f, 1.5f, 2.0f, 2.8f, 4.0f, 6.0f, 9.0f, 13.5f, 20.0f };
39 using KODI::UTILS::COLOR::Color;
41 CSlideShowPic::CSlideShowPic() : m_pImage(nullptr)
43 m_bIsLoaded = false;
44 m_bDrawNextImage = false;
45 m_bTransitionImmediately = false;
47 m_bCanMoveHorizontally = false;
48 m_bCanMoveVertically = false;
51 CSlideShowPic::~CSlideShowPic()
53 Close();
56 void CSlideShowPic::Close()
58 std::unique_lock<CCriticalSection> lock(m_textureAccess);
59 m_pImage.reset();
60 m_bIsLoaded = false;
61 m_bDrawNextImage = false;
62 m_bTransitionImmediately = false;
63 m_bIsDirty = true;
64 m_alpha = 0;
67 void CSlideShowPic::Reset(DISPLAY_EFFECT dispEffect, TRANSITION_EFFECT transEffect)
69 std::unique_lock<CCriticalSection> lock(m_textureAccess);
70 if (m_pImage)
71 SetTexture_Internal(m_iSlideNumber, std::move(m_pImage), dispEffect, transEffect);
72 else
73 Close();
76 bool CSlideShowPic::DisplayEffectNeedChange(DISPLAY_EFFECT newDispEffect) const
78 if (m_displayEffect == newDispEffect)
79 return false;
80 if (newDispEffect == EFFECT_RANDOM && m_displayEffect != EFFECT_NONE && m_displayEffect != EFFECT_NO_TIMEOUT)
81 return false;
82 return true;
85 bool CSlideShowPic::IsFinished() const
87 return IsLoaded() && m_iCounter >= m_transitionEnd.start + m_transitionEnd.length;
90 bool CSlideShowPic::IsAnimating() const
92 return !IsFinished() &&
93 (m_displayEffect != EFFECT_NO_TIMEOUT || // Special snowflake, doesn't work without this
94 m_iCounter < m_transitionStart.length || // Inside start transition
95 m_iCounter >= m_transitionEnd.start || // Inside end transition
96 (m_iCounter >= m_transitionTemp.start &&
97 m_iCounter < m_transitionTemp.start + m_transitionTemp.length)); // Inside display effect
100 void CSlideShowPic::SetTexture(int iSlideNumber,
101 std::unique_ptr<CTexture> pTexture,
102 DISPLAY_EFFECT dispEffect,
103 TRANSITION_EFFECT transEffect)
105 std::unique_lock<CCriticalSection> lock(m_textureAccess);
106 Close();
107 SetTexture_Internal(iSlideNumber, std::move(pTexture), dispEffect, transEffect);
110 void CSlideShowPic::SetTexture_Internal(int iSlideNumber,
111 std::unique_ptr<CTexture> pTexture,
112 DISPLAY_EFFECT dispEffect,
113 TRANSITION_EFFECT transEffect)
115 std::unique_lock<CCriticalSection> lock(m_textureAccess);
116 m_bPause = false;
117 m_bNoEffect = false;
118 m_bTransitionImmediately = false;
119 m_iSlideNumber = iSlideNumber;
121 m_bIsDirty = true;
122 m_pImage = std::move(pTexture);
123 m_fWidth = static_cast<float>(m_pImage->GetWidth());
124 m_fHeight = static_cast<float>(m_pImage->GetHeight());
125 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_HIGHQUALITYDOWNSCALING))
126 { // activate mipmapping when high quality downscaling is 'on'
127 m_pImage->SetMipmapping();
129 // reset our counter
130 m_iCounter = 0;
131 // initialize our transition effect
132 m_transitionStart.type = transEffect;
133 m_transitionStart.start = 0;
135 // initialize our display effect
136 if (dispEffect == EFFECT_RANDOM)
138 if (((m_fWidth / m_fHeight) > 1.9f) || ((m_fHeight / m_fWidth) > 1.9f))
139 m_displayEffect = EFFECT_PANORAMA;
140 else
141 m_displayEffect = (DISPLAY_EFFECT)((rand() % (EFFECT_RANDOM - 1)) + 1);
143 else
144 m_displayEffect = dispEffect;
146 // the +1's make sure it actually occurs
147 float fadeTime = 0.2f;
148 if (m_displayEffect != EFFECT_NO_TIMEOUT)
149 fadeTime = std::min(0.2f*CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME), 3.0f);
150 m_transitionStart.length = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * fadeTime); // transition time in frames
151 m_transitionEnd.type = transEffect;
152 m_transitionEnd.length = m_transitionStart.length;
153 m_transitionTemp.type = TRANSITION_NONE;
154 m_fTransitionAngle = 0;
155 m_fTransitionZoom = 0;
156 m_fAngle = 0.0f;
157 if (m_pImage->GetOrientation() == 7)
158 { // rotate to 270 degrees
159 m_fAngle = 270.0f;
161 if (m_pImage->GetOrientation() == 2)
162 { // rotate to 180 degrees
163 m_fAngle = 180.0f;
165 if (m_pImage->GetOrientation() == 5)
166 { // rotate to 90 degrees
167 m_fAngle = 90.0f;
169 m_fZoomAmount = 1;
170 m_fZoomLeft = 0;
171 m_fZoomTop = 0;
172 m_fPosX = m_fPosY = 0.0f;
173 m_fPosZ = 1.0f;
174 m_fVelocityX = m_fVelocityY = m_fVelocityZ = 0.0f;
175 int iFrames = std::max((int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME)), 1);
176 if (m_displayEffect == EFFECT_PANORAMA)
178 RESOLUTION_INFO res = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
179 float fScreenWidth = (float)res.Overscan.right - res.Overscan.left;
180 float fScreenHeight = (float)res.Overscan.bottom - res.Overscan.top;
182 if (m_fWidth > m_fHeight)
184 iFrames = (int)(iFrames * (m_fWidth - m_fHeight) / m_fHeight);
185 m_iTotalFrames = m_transitionStart.length + m_transitionEnd.length + iFrames;
187 m_fPosX = 0.5f - (fScreenWidth / fScreenHeight) * (m_fHeight / m_fWidth) * 0.5f;
188 if (rand() % 2)
189 m_fPosX = -m_fPosX;
190 m_fVelocityX = -m_fPosX * 2.0f / m_iTotalFrames;
192 else
194 iFrames = (int)(iFrames * (m_fHeight - (0.5f * m_fWidth)) / m_fWidth);
195 m_iTotalFrames = m_transitionStart.length + m_transitionEnd.length + iFrames;
197 m_fPosY = 0.5f - (fScreenHeight / fScreenWidth) * (m_fWidth / m_fHeight) * 0.5f;
198 if (rand() % 2)
199 m_fPosY = -m_fPosY;
200 m_fVelocityY = -m_fPosY * 2.0f / m_iTotalFrames;
203 else
205 m_iTotalFrames = m_transitionStart.length + m_transitionEnd.length + iFrames;
207 if (m_displayEffect == EFFECT_FLOAT)
209 // Calculate start and end positions
210 // choose a random direction
211 float angle = (rand() % 1000) / 1000.0f * 2 * (float)M_PI;
212 m_fPosX = cos(angle) * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.00005f;
213 m_fPosY = sin(angle) * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.00005f;
214 m_fVelocityX = -m_fPosX * 2.0f / m_iTotalFrames;
215 m_fVelocityY = -m_fPosY * 2.0f / m_iTotalFrames;
217 else if (m_displayEffect == EFFECT_ZOOM)
219 m_fPosZ = 1.0f;
220 m_fVelocityZ = 0.0001f * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowZoomAmount;
224 m_transitionEnd.start = m_transitionStart.length + iFrames;
226 m_bDrawNextImage = false;
227 m_bIsLoaded = true;
230 void CSlideShowPic::SetOriginalSize(int iOriginalWidth, int iOriginalHeight, bool bFullSize)
232 m_iOriginalWidth = iOriginalWidth;
233 m_iOriginalHeight = iOriginalHeight;
234 m_bFullSize = bFullSize;
237 int CSlideShowPic::GetOriginalWidth()
239 int iAngle = (int)(m_fAngle / 90.0f + 0.4f);
240 if (iAngle % 2)
241 return m_iOriginalHeight;
242 else
243 return m_iOriginalWidth;
246 int CSlideShowPic::GetOriginalHeight()
248 int iAngle = (int)(m_fAngle / 90.0f + 0.4f);
249 if (iAngle % 2)
250 return m_iOriginalWidth;
251 else
252 return m_iOriginalHeight;
255 void CSlideShowPic::UpdateTexture(std::unique_ptr<CTexture> pTexture)
257 std::unique_lock<CCriticalSection> lock(m_textureAccess);
258 m_pImage = std::move(pTexture);
259 m_fWidth = static_cast<float>(m_pImage->GetWidth());
260 m_fHeight = static_cast<float>(m_pImage->GetHeight());
261 m_bIsDirty = true;
264 static CRect GetRectangle(const float x[4], const float y[4])
266 CRect rect;
267 rect.x1 = *std::min_element(x, x+4);
268 rect.y1 = *std::min_element(y, y+4);
269 rect.x2 = *std::max_element(x, x+4);
270 rect.y2 = *std::max_element(y, y+4);
271 return rect;
274 void CSlideShowPic::UpdateVertices(float cur_x[4], float cur_y[4], const float new_x[4], const float new_y[4], CDirtyRegionList &dirtyregions)
276 const size_t count = sizeof(float)*4;
277 if(memcmp(cur_x, new_x, count)
278 || memcmp(cur_y, new_y, count)
279 || m_bIsDirty)
281 dirtyregions.emplace_back(GetRectangle(cur_x, cur_y));
282 dirtyregions.emplace_back(GetRectangle(new_x, new_y));
283 memcpy(cur_x, new_x, count);
284 memcpy(cur_y, new_y, count);
288 void CSlideShowPic::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
290 if (!m_pImage || !m_bIsLoaded || IsFinished())
291 return;
293 UpdateAlpha();
295 bool bPaused = m_bPause | (m_fZoomAmount != 1.0f);
296 // check if we're doing a temporary effect (such as rotate + zoom)
297 if (m_transitionTemp.type != TRANSITION_NONE)
299 bPaused = true;
300 if (m_iCounter >= m_transitionTemp.start)
302 if (m_iCounter >= m_transitionTemp.start + m_transitionTemp.length)
303 { // we're finished this transition
304 if (m_transitionTemp.type == TRANSITION_ZOOM)
305 { // correct for any introduced inaccuracies.
306 int i;
307 for (i = 0; i < 10; i++)
309 if (fabs(m_fZoomAmount - zoomamount[i]) < 0.01f * zoomamount[i])
311 m_fZoomAmount = zoomamount[i];
312 break;
315 m_bNoEffect = (m_fZoomAmount != 1.0f); // turn effect rendering back on.
317 m_transitionTemp.type = TRANSITION_NONE;
319 else
321 if (m_transitionTemp.type == TRANSITION_ROTATE)
322 m_fAngle += m_fTransitionAngle;
323 if (m_transitionTemp.type == TRANSITION_ZOOM)
324 m_fZoomAmount += m_fTransitionZoom;
328 // now just display
329 if (!m_bNoEffect && !bPaused)
331 if (m_displayEffect == EFFECT_PANORAMA)
333 m_fPosX += m_fVelocityX;
334 m_fPosY += m_fVelocityY;
336 else if (m_displayEffect == EFFECT_FLOAT)
338 m_fPosX += m_fVelocityX;
339 m_fPosY += m_fVelocityY;
340 float fMoveAmount = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.0001f;
341 if (m_fPosX > fMoveAmount)
343 m_fPosX = fMoveAmount;
344 m_fVelocityX = -m_fVelocityX;
346 if (m_fPosX < -fMoveAmount)
348 m_fPosX = -fMoveAmount;
349 m_fVelocityX = -m_fVelocityX;
351 if (m_fPosY > fMoveAmount)
353 m_fPosY = fMoveAmount;
354 m_fVelocityY = -m_fVelocityY;
356 if (m_fPosY < -fMoveAmount)
358 m_fPosY = -fMoveAmount;
359 m_fVelocityY = -m_fVelocityY;
362 else if (m_displayEffect == EFFECT_ZOOM)
364 m_fPosZ += m_fVelocityZ;
365 /* if (m_fPosZ > 1.0f + 0.01f*CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("Slideshow.ZoomAmount"))
367 m_fPosZ = 1.0f + 0.01f * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("Slideshow.ZoomAmount");
368 m_fVelocityZ = -m_fVelocityZ;
370 if (m_fPosZ < 1.0f)
372 m_fPosZ = 1.0f;
373 m_fVelocityZ = -m_fVelocityZ;
377 if (m_displayEffect != EFFECT_NO_TIMEOUT && bPaused && !m_bTransitionImmediately)
378 { // paused - increment the last transition start time
379 m_transitionEnd.start++;
381 if (m_iCounter >= m_transitionEnd.start)
382 m_bDrawNextImage = true;
383 if (IsAnimating())
385 /* this really annoying. there's non-stop logging when viewing a pic outside of the slideshow
386 if (m_displayEffect == EFFECT_NO_TIMEOUT)
387 CLog::Log(LOGDEBUG, "Incrementing counter ({}) while not in slideshow (startlength={},endstart={},endlength={})", m_iCounter, m_transitionStart.length, m_transitionEnd.start, m_transitionEnd.length);
389 m_iCounter++;
392 RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
394 // calculate where we should render (and how large it should be)
395 // calculate aspect ratio correction factor
396 float fOffsetX = (float)info.Overscan.left;
397 float fOffsetY = (float)info.Overscan.top;
398 float fScreenWidth = (float)info.Overscan.right - info.Overscan.left;
399 float fScreenHeight = (float)info.Overscan.bottom - info.Overscan.top;
400 float fPixelRatio = info.fPixelRatio;
402 // Rotate the image as needed
403 float x[4];
404 float y[4];
405 float si = sin(m_fAngle / 180.0f * static_cast<float>(M_PI));
406 float co = cos(m_fAngle / 180.0f * static_cast<float>(M_PI));
407 x[0] = -m_fWidth * co + m_fHeight * si;
408 y[0] = -m_fWidth * si - m_fHeight * co;
409 x[1] = m_fWidth * co + m_fHeight * si;
410 y[1] = m_fWidth * si - m_fHeight * co;
411 x[2] = m_fWidth * co - m_fHeight * si;
412 y[2] = m_fWidth * si + m_fHeight * co;
413 x[3] = -m_fWidth * co - m_fHeight * si;
414 y[3] = -m_fWidth * si + m_fHeight * co;
416 // calculate our scale amounts
417 float fSourceAR = m_fWidth / m_fHeight;
418 float fSourceInvAR = 1 / fSourceAR;
419 float fAR = si * si * (fSourceInvAR - fSourceAR) + fSourceAR;
421 //float fOutputFrameAR = fAR / fPixelRatio;
423 float fScaleNorm = fScreenWidth / m_fWidth;
424 float fScaleInv = fScreenWidth / m_fHeight;
426 bool bFillScreen = false;
427 float fComp = 1.0f + 0.01f * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowBlackBarCompensation;
428 float fScreenRatio = fScreenWidth / fScreenHeight * fPixelRatio;
429 // work out if we should be compensating the zoom to minimize blackbars
430 // we should compute this based on the % of black bars on screen perhaps??
431 //! @todo change m_displayEffect != EFFECT_NO_TIMEOUT to whether we're running the slideshow
432 if (m_displayEffect != EFFECT_NO_TIMEOUT && m_displayEffect != EFFECT_NONE && fScreenRatio < fSourceAR * fComp && fSourceAR < fScreenRatio * fComp)
433 bFillScreen = true;
434 if ((!bFillScreen && fScreenWidth*fPixelRatio > fScreenHeight*fSourceAR) || (bFillScreen && fScreenWidth*fPixelRatio < fScreenHeight*fSourceAR))
435 fScaleNorm = fScreenHeight / (m_fHeight * fPixelRatio);
436 bFillScreen = false;
437 if (m_displayEffect != EFFECT_NO_TIMEOUT && m_displayEffect != EFFECT_NONE && fScreenRatio < fSourceInvAR * fComp && fSourceInvAR < fScreenRatio * fComp)
438 bFillScreen = true;
439 if ((!bFillScreen && fScreenWidth*fPixelRatio > fScreenHeight*fSourceInvAR) || (bFillScreen && fScreenWidth*fPixelRatio < fScreenHeight*fSourceInvAR))
440 fScaleInv = fScreenHeight / (m_fWidth * fPixelRatio);
442 float fScale = si * si * (fScaleInv - fScaleNorm) + fScaleNorm;
443 // scale if we need to due to the effect we're using
444 if (m_displayEffect == EFFECT_PANORAMA)
446 if (m_fWidth > m_fHeight)
447 fScale *= m_fWidth / fScreenWidth * fScreenHeight / m_fHeight;
448 else
449 fScale *= m_fHeight / fScreenHeight * fScreenWidth / m_fWidth;
451 if (m_displayEffect == EFFECT_FLOAT)
452 fScale *= (1.0f + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.0001f);
453 if (m_displayEffect == EFFECT_ZOOM)
454 fScale *= m_fPosZ;
455 // zoom image
456 fScale *= m_fZoomAmount;
458 // calculate the resultant coordinates
459 for (int i = 0; i < 4; i++)
461 x[i] *= fScale * 0.5f; // as the offsets x[] and y[] are from center
462 y[i] *= fScale * fPixelRatio * 0.5f;
463 // center it
464 x[i] += 0.5f * fScreenWidth + fOffsetX;
465 y[i] += 0.5f * fScreenHeight + fOffsetY;
467 // shift if we're zooming
468 if (m_fZoomAmount > 1)
470 float minx = x[0];
471 float maxx = x[0];
472 float miny = y[0];
473 float maxy = y[0];
474 for (int i = 1; i < 4; i++)
476 if (x[i] < minx) minx = x[i];
477 if (x[i] > maxx) maxx = x[i];
478 if (y[i] < miny) miny = y[i];
479 if (y[i] > maxy) maxy = y[i];
481 float w = maxx - minx;
482 float h = maxy - miny;
483 m_bCanMoveHorizontally = (w >= fScreenWidth);
484 m_bCanMoveVertically = (h >= fScreenHeight);
485 if (w >= fScreenWidth)
486 { // must have no black bars
487 if (minx + m_fZoomLeft*w > fOffsetX)
488 m_fZoomLeft = (fOffsetX - minx) / w;
489 if (maxx + m_fZoomLeft*w < fOffsetX + fScreenWidth)
490 m_fZoomLeft = (fScreenWidth + fOffsetX - maxx) / w;
491 for (float& i : x)
492 i += w * m_fZoomLeft;
494 if (h >= fScreenHeight)
495 { // must have no black bars
496 if (miny + m_fZoomTop*h > fOffsetY)
497 m_fZoomTop = (fOffsetY - miny) / h;
498 if (maxy + m_fZoomTop*h < fOffsetY + fScreenHeight)
499 m_fZoomTop = (fScreenHeight + fOffsetY - maxy) / h;
500 for (float& i : y)
501 i += m_fZoomTop * h;
504 // add offset from display effects
505 for (int i = 0; i < 4; i++)
507 x[i] += m_fPosX * m_fWidth * fScale;
508 y[i] += m_fPosY * m_fHeight * fScale;
511 UpdateVertices(m_ax, m_ay, x, y, dirtyregions);
513 // now render the image in the top right corner if we're zooming
514 if (m_fZoomAmount == 1 || m_bIsComic)
516 const float empty[4] = {};
517 UpdateVertices(m_bx, m_by, empty, empty, dirtyregions);
518 UpdateVertices(m_sx, m_sy, empty, empty, dirtyregions);
519 UpdateVertices(m_ox, m_oy, empty, empty, dirtyregions);
520 m_bIsDirty = false;
521 return;
524 float sx[4], sy[4];
525 sx[0] = -m_fWidth * co + m_fHeight * si;
526 sy[0] = -m_fWidth * si - m_fHeight * co;
527 sx[1] = m_fWidth * co + m_fHeight * si;
528 sy[1] = m_fWidth * si - m_fHeight * co;
529 sx[2] = m_fWidth * co - m_fHeight * si;
530 sy[2] = m_fWidth * si + m_fHeight * co;
531 sx[3] = -m_fWidth * co - m_fHeight * si;
532 sy[3] = -m_fWidth * si + m_fHeight * co;
533 // convert to the appropriate scale
534 float fSmallArea = fScreenWidth * fScreenHeight / 50;
535 float fSmallWidth = sqrt(fSmallArea * fAR / fPixelRatio); // fAR*height = width, so total area*far = width*width
536 float fSmallHeight = fSmallArea / fSmallWidth;
537 float fSmallX = fOffsetX + fScreenWidth * 0.95f - fSmallWidth * 0.5f;
538 float fSmallY = fOffsetY + fScreenHeight * 0.05f + fSmallHeight * 0.5f;
539 fScaleNorm = fSmallWidth / m_fWidth;
540 fScaleInv = fSmallWidth / m_fHeight;
541 fScale = si * si * (fScaleInv - fScaleNorm) + fScaleNorm;
542 for (int i = 0; i < 4; i++)
544 sx[i] *= fScale * 0.5f;
545 sy[i] *= fScale * fPixelRatio * 0.5f;
547 // calculate a black border
548 float bx[4];
549 float by[4];
550 for (int i = 0; i < 4; i++)
552 if (sx[i] > 0)
553 bx[i] = sx[i] + 1;
554 else
555 bx[i] = sx[i] - 1;
556 if (sy[i] > 0)
557 by[i] = sy[i] + 1;
558 else
559 by[i] = sy[i] - 1;
560 sx[i] += fSmallX;
561 sy[i] += fSmallY;
562 bx[i] += fSmallX;
563 by[i] += fSmallY;
566 fSmallX -= fSmallWidth * 0.5f;
567 fSmallY -= fSmallHeight * 0.5f;
569 UpdateVertices(m_bx, m_by, bx, by, dirtyregions);
570 UpdateVertices(m_sx, m_sy, sx, sy, dirtyregions);
572 // now we must render the wireframe image of the view window
573 // work out the direction of the top of pic vector
574 float scale;
575 if (fabs(x[1] - x[0]) > fabs(x[3] - x[0]))
576 scale = (sx[1] - sx[0]) / (x[1] - x[0]);
577 else
578 scale = (sx[3] - sx[0]) / (x[3] - x[0]);
579 float ox[4];
580 float oy[4];
581 ox[0] = (fOffsetX - x[0]) * scale + sx[0];
582 oy[0] = (fOffsetY - y[0]) * scale + sy[0];
583 ox[1] = (fOffsetX + fScreenWidth - x[0]) * scale + sx[0];
584 oy[1] = (fOffsetY - y[0]) * scale + sy[0];
585 ox[2] = (fOffsetX + fScreenWidth - x[0]) * scale + sx[0];
586 oy[2] = (fOffsetY + fScreenHeight - y[0]) * scale + sy[0];
587 ox[3] = (fOffsetX - x[0]) * scale + sx[0];
588 oy[3] = (fOffsetY + fScreenHeight - y[0]) * scale + sy[0];
589 // crop to within the range of our piccy
590 for (int i = 0; i < 4; i++)
592 if (ox[i] < fSmallX) ox[i] = fSmallX;
593 if (ox[i] > fSmallX + fSmallWidth) ox[i] = fSmallX + fSmallWidth;
594 if (oy[i] < fSmallY) oy[i] = fSmallY;
595 if (oy[i] > fSmallY + fSmallHeight) oy[i] = fSmallY + fSmallHeight;
598 UpdateVertices(m_ox, m_oy, ox, oy, dirtyregions);
599 m_bIsDirty = false;
602 void CSlideShowPic::Keep()
604 // this is called if we need to keep the current pic on screen
605 // to wait for the next pic to load
606 if (!m_bDrawNextImage) return ; // don't need to keep pic
607 // hold off the start of the next frame
608 m_transitionEnd.start = m_iCounter;
611 bool CSlideShowPic::StartTransition()
613 // this is called if we need to start transitioning immediately to the new picture
614 if (m_bDrawNextImage) return false; // don't need to do anything as we are already transitioning
615 // decrease the number of display frame
616 m_transitionEnd.start = m_iCounter;
617 m_bTransitionImmediately = true;
618 return true;
621 void CSlideShowPic::Pause(bool bPause)
623 if (!m_bDrawNextImage)
624 m_bPause = bPause;
627 void CSlideShowPic::SetInSlideshow(bool slideshow)
629 if (slideshow && m_displayEffect == EFFECT_NO_TIMEOUT)
630 m_displayEffect = EFFECT_NONE;
633 int CSlideShowPic::GetTransitionTime(int iType) const
635 if (iType == 0) // start transition
636 return m_transitionStart.length;
637 else // iType == 1 // end transition
638 return m_transitionEnd.length;
641 void CSlideShowPic::SetTransitionTime(int iType, int iTime)
643 if (iType == 0) // start transition
644 m_transitionStart.length = iTime;
645 else // iType == 1 // end transition
646 m_transitionEnd.length = iTime;
649 void CSlideShowPic::Rotate(float fRotateAngle, bool immediate /* = false */)
651 if (m_bDrawNextImage) return;
652 if (m_transitionTemp.type == TRANSITION_ZOOM) return;
653 if (immediate)
655 m_fAngle += fRotateAngle;
656 return;
659 // if there is a rotation ongoing already
660 // add the new angle to the old destination angle
661 if (m_transitionTemp.type == TRANSITION_ROTATE &&
662 m_transitionTemp.start + m_transitionTemp.length > m_iCounter)
664 int remainder = m_transitionTemp.start + m_transitionTemp.length - m_iCounter;
665 fRotateAngle += m_fTransitionAngle * remainder;
668 m_transitionTemp.type = TRANSITION_ROTATE;
669 m_transitionTemp.start = m_iCounter;
670 m_transitionTemp.length = IMMEDIATE_TRANSITION_TIME;
671 m_fTransitionAngle = fRotateAngle / (float)m_transitionTemp.length;
672 // reset the timer
673 m_transitionEnd.start = m_iCounter + m_transitionStart.length + (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME));
676 void CSlideShowPic::Zoom(float fZoom, bool immediate /* = false */)
678 if (m_bDrawNextImage) return;
679 if (m_transitionTemp.type == TRANSITION_ROTATE) return;
680 if (immediate)
682 m_fZoomAmount = fZoom;
683 return;
685 m_transitionTemp.type = TRANSITION_ZOOM;
686 m_transitionTemp.start = m_iCounter;
687 m_transitionTemp.length = IMMEDIATE_TRANSITION_TIME;
688 m_fTransitionZoom = (fZoom - m_fZoomAmount) / (float)m_transitionTemp.length;
689 // reset the timer
690 m_transitionEnd.start = m_iCounter + m_transitionStart.length + (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME));
691 // turn off the render effects until we're back down to normal zoom
692 m_bNoEffect = true;
695 void CSlideShowPic::UpdateAlpha()
697 assert(m_iCounter >= 0);
699 Color alpha = m_alpha;
701 if (m_iCounter < m_transitionStart.length)
702 { // do start transition
703 switch (m_transitionStart.type)
705 case CROSSFADE:
706 // fade in at 1x speed
707 alpha = static_cast<Color>(static_cast<float>(m_iCounter + 1) /
708 static_cast<float>(m_transitionStart.length) * 255.0f);
709 break;
710 case FADEIN_FADEOUT:
711 // fade in at 2x speed, then keep solid
712 alpha =
713 std::min(static_cast<Color>(static_cast<float>(m_iCounter + 1) /
714 static_cast<float>(m_transitionStart.length) * 255.0f * 2),
715 Color{255});
716 break;
717 default:
718 alpha = 255; // opaque
722 if (m_iCounter >= m_transitionEnd.start)
723 { // do end transition
724 switch (m_transitionEnd.type)
726 case CROSSFADE:
727 // fade in at 1x speed
728 alpha =
729 255 - static_cast<Color>(static_cast<float>(m_iCounter - m_transitionEnd.start + 1) /
730 static_cast<float>(m_transitionEnd.length) * 255.0f);
731 break;
732 case FADEIN_FADEOUT:
733 // fade in at 2x speed, then keep solid
734 alpha =
735 std::min(static_cast<Color>(static_cast<float>(m_transitionEnd.length - m_iCounter +
736 m_transitionEnd.start + 1) /
737 static_cast<float>(m_transitionEnd.length) * 255.0f * 2),
738 Color{255});
739 break;
740 default:
741 alpha = 255; // opaque
745 if (alpha != m_alpha)
747 m_alpha = alpha;
748 m_bIsDirty = true;
752 void CSlideShowPic::Move(float fDeltaX, float fDeltaY)
754 m_fZoomLeft += fDeltaX;
755 m_fZoomTop += fDeltaY;
756 // reset the timer
757 // m_transitionEnd.start = m_iCounter + m_transitionStart.length + (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME));
760 void CSlideShowPic::Render()
762 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetRenderOrder() ==
763 RENDER_ORDER_FRONT_TO_BACK)
764 return;
765 std::unique_lock<CCriticalSection> lock(m_textureAccess);
767 Render(m_ax, m_ay, m_pImage.get(), (m_alpha << 24) | 0xFFFFFF);
769 // now render the image in the top right corner if we're zooming
770 if (m_fZoomAmount == 1.0f || m_bIsComic) return ;
772 Render(m_bx, m_by, NULL, PICTURE_VIEW_BOX_BACKGROUND);
773 Render(m_sx, m_sy, m_pImage.get(), 0xFFFFFFFF);
774 Render(m_ox, m_oy, NULL, PICTURE_VIEW_BOX_COLOR);