diff options
Diffstat (limited to 'xbmc/pictures/SlideShowPicture.cpp')
-rw-r--r-- | xbmc/pictures/SlideShowPicture.cpp | 1016 |
1 files changed, 1016 insertions, 0 deletions
diff --git a/xbmc/pictures/SlideShowPicture.cpp b/xbmc/pictures/SlideShowPicture.cpp new file mode 100644 index 0000000..63ec8b9 --- /dev/null +++ b/xbmc/pictures/SlideShowPicture.cpp @@ -0,0 +1,1016 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "SlideShowPicture.h" + +#include "ServiceBroker.h" +#include "guilib/Texture.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "windowing/GraphicContext.h" +#include "windowing/WinSystem.h" + +#include <mutex> + +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES +#endif +#include <math.h> + +#if defined(HAS_GL) +#include "rendering/gl/RenderSystemGL.h" +#include "utils/GLUtils.h" +#elif defined(HAS_GLES) +#include "rendering/gles/RenderSystemGLES.h" +#include "utils/GLUtils.h" +#elif defined(TARGET_WINDOWS) +#include "guilib/TextureDX.h" +#include "rendering/dx/DeviceResources.h" +#include "rendering/dx/RenderContext.h" + +#include <DirectXMath.h> +using namespace DirectX; +using namespace Microsoft::WRL; +#endif + +#include <cstddef> + +#define IMMEDIATE_TRANSITION_TIME 20 + +#define PICTURE_MOVE_AMOUNT 0.02f +#define PICTURE_MOVE_AMOUNT_ANALOG 0.01f +#define PICTURE_VIEW_BOX_COLOR 0xffffff00 // YELLOW +#define PICTURE_VIEW_BOX_BACKGROUND 0xff000000 // BLACK + +#define FPS 25 + +static float zoomamount[10] = { 1.0f, 1.2f, 1.5f, 2.0f, 2.8f, 4.0f, 6.0f, 9.0f, 13.5f, 20.0f }; + +CSlideShowPic::CSlideShowPic() : m_pImage(nullptr) +{ + m_bIsLoaded = false; + m_bIsFinished = false; + m_bDrawNextImage = false; + m_bTransitionImmediately = false; + + m_bCanMoveHorizontally = false; + m_bCanMoveVertically = false; +} + +CSlideShowPic::~CSlideShowPic() +{ + Close(); +} + +void CSlideShowPic::Close() +{ + std::unique_lock<CCriticalSection> lock(m_textureAccess); + m_pImage.reset(); + m_bIsLoaded = false; + m_bIsFinished = false; + m_bDrawNextImage = false; + m_bTransitionImmediately = false; + m_bIsDirty = true; + m_alpha = 0; +#ifdef HAS_DX + m_vb = nullptr; +#endif +} + +void CSlideShowPic::Reset(DISPLAY_EFFECT dispEffect, TRANSITION_EFFECT transEffect) +{ + std::unique_lock<CCriticalSection> lock(m_textureAccess); + if (m_pImage) + SetTexture_Internal(m_iSlideNumber, std::move(m_pImage), dispEffect, transEffect); + else + Close(); +} + +bool CSlideShowPic::DisplayEffectNeedChange(DISPLAY_EFFECT newDispEffect) const +{ + if (m_displayEffect == newDispEffect) + return false; + if (newDispEffect == EFFECT_RANDOM && m_displayEffect != EFFECT_NONE && m_displayEffect != EFFECT_NO_TIMEOUT) + return false; + return true; +} + +void CSlideShowPic::SetTexture(int iSlideNumber, + std::unique_ptr<CTexture> pTexture, + DISPLAY_EFFECT dispEffect, + TRANSITION_EFFECT transEffect) +{ + std::unique_lock<CCriticalSection> lock(m_textureAccess); + Close(); + SetTexture_Internal(iSlideNumber, std::move(pTexture), dispEffect, transEffect); +} + +void CSlideShowPic::SetTexture_Internal(int iSlideNumber, + std::unique_ptr<CTexture> pTexture, + DISPLAY_EFFECT dispEffect, + TRANSITION_EFFECT transEffect) +{ + std::unique_lock<CCriticalSection> lock(m_textureAccess); + m_bPause = false; + m_bNoEffect = false; + m_bTransitionImmediately = false; + m_iSlideNumber = iSlideNumber; + + m_bIsDirty = true; + m_pImage = std::move(pTexture); + m_fWidth = static_cast<float>(m_pImage->GetWidth()); + m_fHeight = static_cast<float>(m_pImage->GetHeight()); + if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_HIGHQUALITYDOWNSCALING)) + { // activate mipmapping when high quality downscaling is 'on' + m_pImage->SetMipmapping(); + } + // reset our counter + m_iCounter = 0; + // initialize our transition effect + m_transitionStart.type = transEffect; + m_transitionStart.start = 0; + + // initialize our display effect + if (dispEffect == EFFECT_RANDOM) + { + if (((m_fWidth / m_fHeight) > 1.9f) || ((m_fHeight / m_fWidth) > 1.9f)) + m_displayEffect = EFFECT_PANORAMA; + else + m_displayEffect = (DISPLAY_EFFECT)((rand() % (EFFECT_RANDOM - 1)) + 1); + } + else + m_displayEffect = dispEffect; + + // the +1's make sure it actually occurs + float fadeTime = 0.2f; + if (m_displayEffect != EFFECT_NO_TIMEOUT) + fadeTime = std::min(0.2f*CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME), 3.0f); + m_transitionStart.length = (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * fadeTime); // transition time in frames + m_transitionEnd.type = transEffect; + m_transitionEnd.length = m_transitionStart.length; + m_transitionTemp.type = TRANSITION_NONE; + m_fTransitionAngle = 0; + m_fTransitionZoom = 0; + m_fAngle = 0.0f; + if (m_pImage->GetOrientation() == 7) + { // rotate to 270 degrees + m_fAngle = 270.0f; + } + if (m_pImage->GetOrientation() == 2) + { // rotate to 180 degrees + m_fAngle = 180.0f; + } + if (m_pImage->GetOrientation() == 5) + { // rotate to 90 degrees + m_fAngle = 90.0f; + } + m_fZoomAmount = 1; + m_fZoomLeft = 0; + m_fZoomTop = 0; + m_fPosX = m_fPosY = 0.0f; + m_fPosZ = 1.0f; + m_fVelocityX = m_fVelocityY = m_fVelocityZ = 0.0f; + int iFrames = std::max((int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME)), 1); + if (m_displayEffect == EFFECT_PANORAMA) + { + RESOLUTION_INFO res = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(); + float fScreenWidth = (float)res.Overscan.right - res.Overscan.left; + float fScreenHeight = (float)res.Overscan.bottom - res.Overscan.top; + + if (m_fWidth > m_fHeight) + { + iFrames = (int)(iFrames * (m_fWidth - m_fHeight) / m_fHeight); + m_iTotalFrames = m_transitionStart.length + m_transitionEnd.length + iFrames; + + m_fPosX = 0.5f - (fScreenWidth / fScreenHeight) * (m_fHeight / m_fWidth) * 0.5f; + if (rand() % 2) + m_fPosX = -m_fPosX; + m_fVelocityX = -m_fPosX * 2.0f / m_iTotalFrames; + } + else + { + iFrames = (int)(iFrames * (m_fHeight - (0.5f * m_fWidth)) / m_fWidth); + m_iTotalFrames = m_transitionStart.length + m_transitionEnd.length + iFrames; + + m_fPosY = 0.5f - (fScreenHeight / fScreenWidth) * (m_fWidth / m_fHeight) * 0.5f; + if (rand() % 2) + m_fPosY = -m_fPosY; + m_fVelocityY = -m_fPosY * 2.0f / m_iTotalFrames; + } + } + else + { + m_iTotalFrames = m_transitionStart.length + m_transitionEnd.length + iFrames; + + if (m_displayEffect == EFFECT_FLOAT) + { + // Calculate start and end positions + // choose a random direction + float angle = (rand() % 1000) / 1000.0f * 2 * (float)M_PI; + m_fPosX = cos(angle) * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.00005f; + m_fPosY = sin(angle) * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.00005f; + m_fVelocityX = -m_fPosX * 2.0f / m_iTotalFrames; + m_fVelocityY = -m_fPosY * 2.0f / m_iTotalFrames; + } + else if (m_displayEffect == EFFECT_ZOOM) + { + m_fPosZ = 1.0f; + m_fVelocityZ = 0.0001f * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowZoomAmount; + } + } + + m_transitionEnd.start = m_transitionStart.length + iFrames; + + m_bIsFinished = false; + m_bDrawNextImage = false; + m_bIsLoaded = true; +} + +void CSlideShowPic::SetOriginalSize(int iOriginalWidth, int iOriginalHeight, bool bFullSize) +{ + m_iOriginalWidth = iOriginalWidth; + m_iOriginalHeight = iOriginalHeight; + m_bFullSize = bFullSize; +} + +int CSlideShowPic::GetOriginalWidth() +{ + int iAngle = (int)(m_fAngle / 90.0f + 0.4f); + if (iAngle % 2) + return m_iOriginalHeight; + else + return m_iOriginalWidth; +} + +int CSlideShowPic::GetOriginalHeight() +{ + int iAngle = (int)(m_fAngle / 90.0f + 0.4f); + if (iAngle % 2) + return m_iOriginalWidth; + else + return m_iOriginalHeight; +} + +void CSlideShowPic::UpdateTexture(std::unique_ptr<CTexture> pTexture) +{ + std::unique_lock<CCriticalSection> lock(m_textureAccess); + m_pImage = std::move(pTexture); + m_fWidth = static_cast<float>(m_pImage->GetWidth()); + m_fHeight = static_cast<float>(m_pImage->GetHeight()); + m_bIsDirty = true; +} + +static CRect GetRectangle(const float x[4], const float y[4]) +{ + CRect rect; + rect.x1 = *std::min_element(x, x+4); + rect.y1 = *std::min_element(y, y+4); + rect.x2 = *std::max_element(x, x+4); + rect.y2 = *std::max_element(y, y+4); + return rect; +} + +void CSlideShowPic::UpdateVertices(float cur_x[4], float cur_y[4], const float new_x[4], const float new_y[4], CDirtyRegionList &dirtyregions) +{ + const size_t count = sizeof(float)*4; + if(memcmp(cur_x, new_x, count) + || memcmp(cur_y, new_y, count) + || m_bIsDirty) + { + dirtyregions.push_back(CDirtyRegion(GetRectangle(cur_x, cur_y))); + dirtyregions.push_back(CDirtyRegion(GetRectangle(new_x, new_y))); + memcpy(cur_x, new_x, count); + memcpy(cur_y, new_y, count); + } +} + +void CSlideShowPic::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + if (!m_pImage || !m_bIsLoaded || m_bIsFinished) return ; + UTILS::COLOR::Color alpha = m_alpha; + if (m_iCounter <= m_transitionStart.length) + { // do start transition + if (m_transitionStart.type == CROSSFADE) + { // fade in at 1x speed + alpha = (UTILS::COLOR::Color)((float)m_iCounter / (float)m_transitionStart.length * 255.0f); + } + else if (m_transitionStart.type == FADEIN_FADEOUT) + { // fade in at 2x speed, then keep solid + alpha = + (UTILS::COLOR::Color)((float)m_iCounter / (float)m_transitionStart.length * 255.0f * 2); + if (alpha > 255) alpha = 255; + } + else // m_transitionEffect == TRANSITION_NONE + { + alpha = 0xFF; // opaque + } + } + bool bPaused = m_bPause | (m_fZoomAmount != 1.0f); + // check if we're doing a temporary effect (such as rotate + zoom) + if (m_transitionTemp.type != TRANSITION_NONE) + { + bPaused = true; + if (m_iCounter >= m_transitionTemp.start) + { + if (m_iCounter >= m_transitionTemp.start + m_transitionTemp.length) + { // we're finished this transition + if (m_transitionTemp.type == TRANSITION_ZOOM) + { // correct for any introduced inaccuracies. + int i; + for (i = 0; i < 10; i++) + { + if (fabs(m_fZoomAmount - zoomamount[i]) < 0.01f * zoomamount[i]) + { + m_fZoomAmount = zoomamount[i]; + break; + } + } + m_bNoEffect = (m_fZoomAmount != 1.0f); // turn effect rendering back on. + } + m_transitionTemp.type = TRANSITION_NONE; + } + else + { + if (m_transitionTemp.type == TRANSITION_ROTATE) + m_fAngle += m_fTransitionAngle; + if (m_transitionTemp.type == TRANSITION_ZOOM) + m_fZoomAmount += m_fTransitionZoom; + } + } + } + // now just display + if (!m_bNoEffect && !bPaused) + { + if (m_displayEffect == EFFECT_PANORAMA) + { + m_fPosX += m_fVelocityX; + m_fPosY += m_fVelocityY; + } + else if (m_displayEffect == EFFECT_FLOAT) + { + m_fPosX += m_fVelocityX; + m_fPosY += m_fVelocityY; + float fMoveAmount = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.0001f; + if (m_fPosX > fMoveAmount) + { + m_fPosX = fMoveAmount; + m_fVelocityX = -m_fVelocityX; + } + if (m_fPosX < -fMoveAmount) + { + m_fPosX = -fMoveAmount; + m_fVelocityX = -m_fVelocityX; + } + if (m_fPosY > fMoveAmount) + { + m_fPosY = fMoveAmount; + m_fVelocityY = -m_fVelocityY; + } + if (m_fPosY < -fMoveAmount) + { + m_fPosY = -fMoveAmount; + m_fVelocityY = -m_fVelocityY; + } + } + else if (m_displayEffect == EFFECT_ZOOM) + { + m_fPosZ += m_fVelocityZ; +/* if (m_fPosZ > 1.0f + 0.01f*CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("Slideshow.ZoomAmount")) + { + m_fPosZ = 1.0f + 0.01f * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("Slideshow.ZoomAmount"); + m_fVelocityZ = -m_fVelocityZ; + } + if (m_fPosZ < 1.0f) + { + m_fPosZ = 1.0f; + m_fVelocityZ = -m_fVelocityZ; + }*/ + } + } + if (m_displayEffect != EFFECT_NO_TIMEOUT && bPaused && !m_bTransitionImmediately) + { // paused - increment the last transition start time + m_transitionEnd.start++; + } + if (m_iCounter >= m_transitionEnd.start) + { // do end transition +// CLog::Log(LOGDEBUG,"Transitioning"); + m_bDrawNextImage = true; + if (m_transitionEnd.type == CROSSFADE) + { // fade out at 1x speed + alpha = 255 - (UTILS::COLOR::Color)((float)(m_iCounter - m_transitionEnd.start) / + (float)m_transitionEnd.length * 255.0f); + } + else if (m_transitionEnd.type == FADEIN_FADEOUT) + { // keep solid, then fade out at 2x speed + alpha = (UTILS::COLOR::Color)( + (float)(m_transitionEnd.length - m_iCounter + m_transitionEnd.start) / + (float)m_transitionEnd.length * 255.0f * 2); + if (alpha > 255) alpha = 255; + } + else // m_transitionEffect == TRANSITION_NONE + { + alpha = 0xFF; // opaque + } + } + if (alpha != m_alpha) + { + m_alpha = alpha; + m_bIsDirty = true; + } + if (m_displayEffect != EFFECT_NO_TIMEOUT || m_iCounter < m_transitionStart.length || m_iCounter >= m_transitionEnd.start || (m_iCounter >= m_transitionTemp.start && m_iCounter < m_transitionTemp.start + m_transitionTemp.length)) + { + /* this really annoying. there's non-stop logging when viewing a pic outside of the slideshow + if (m_displayEffect == EFFECT_NO_TIMEOUT) + CLog::Log(LOGDEBUG, "Incrementing counter ({}) while not in slideshow (startlength={},endstart={},endlength={})", m_iCounter, m_transitionStart.length, m_transitionEnd.start, m_transitionEnd.length); + */ + m_iCounter++; + } + if (m_iCounter > m_transitionEnd.start + m_transitionEnd.length) + m_bIsFinished = true; + + RESOLUTION_INFO info = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(); + + // calculate where we should render (and how large it should be) + // calculate aspect ratio correction factor + float fOffsetX = (float)info.Overscan.left; + float fOffsetY = (float)info.Overscan.top; + float fScreenWidth = (float)info.Overscan.right - info.Overscan.left; + float fScreenHeight = (float)info.Overscan.bottom - info.Overscan.top; + float fPixelRatio = info.fPixelRatio; + + // Rotate the image as needed + float x[4]; + float y[4]; + float si = sin(m_fAngle / 180.0f * static_cast<float>(M_PI)); + float co = cos(m_fAngle / 180.0f * static_cast<float>(M_PI)); + x[0] = -m_fWidth * co + m_fHeight * si; + y[0] = -m_fWidth * si - m_fHeight * co; + x[1] = m_fWidth * co + m_fHeight * si; + y[1] = m_fWidth * si - m_fHeight * co; + x[2] = m_fWidth * co - m_fHeight * si; + y[2] = m_fWidth * si + m_fHeight * co; + x[3] = -m_fWidth * co - m_fHeight * si; + y[3] = -m_fWidth * si + m_fHeight * co; + + // calculate our scale amounts + float fSourceAR = m_fWidth / m_fHeight; + float fSourceInvAR = 1 / fSourceAR; + float fAR = si * si * (fSourceInvAR - fSourceAR) + fSourceAR; + + //float fOutputFrameAR = fAR / fPixelRatio; + + float fScaleNorm = fScreenWidth / m_fWidth; + float fScaleInv = fScreenWidth / m_fHeight; + + bool bFillScreen = false; + float fComp = 1.0f + 0.01f * CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowBlackBarCompensation; + float fScreenRatio = fScreenWidth / fScreenHeight * fPixelRatio; + // work out if we should be compensating the zoom to minimize blackbars + // we should compute this based on the % of black bars on screen perhaps?? + //! @todo change m_displayEffect != EFFECT_NO_TIMEOUT to whether we're running the slideshow + if (m_displayEffect != EFFECT_NO_TIMEOUT && m_displayEffect != EFFECT_NONE && fScreenRatio < fSourceAR * fComp && fSourceAR < fScreenRatio * fComp) + bFillScreen = true; + if ((!bFillScreen && fScreenWidth*fPixelRatio > fScreenHeight*fSourceAR) || (bFillScreen && fScreenWidth*fPixelRatio < fScreenHeight*fSourceAR)) + fScaleNorm = fScreenHeight / (m_fHeight * fPixelRatio); + bFillScreen = false; + if (m_displayEffect != EFFECT_NO_TIMEOUT && m_displayEffect != EFFECT_NONE && fScreenRatio < fSourceInvAR * fComp && fSourceInvAR < fScreenRatio * fComp) + bFillScreen = true; + if ((!bFillScreen && fScreenWidth*fPixelRatio > fScreenHeight*fSourceInvAR) || (bFillScreen && fScreenWidth*fPixelRatio < fScreenHeight*fSourceInvAR)) + fScaleInv = fScreenHeight / (m_fWidth * fPixelRatio); + + float fScale = si * si * (fScaleInv - fScaleNorm) + fScaleNorm; + // scale if we need to due to the effect we're using + if (m_displayEffect == EFFECT_PANORAMA) + { + if (m_fWidth > m_fHeight) + fScale *= m_fWidth / fScreenWidth * fScreenHeight / m_fHeight; + else + fScale *= m_fHeight / fScreenHeight * fScreenWidth / m_fWidth; + } + if (m_displayEffect == EFFECT_FLOAT) + fScale *= (1.0f + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_slideshowPanAmount * m_iTotalFrames * 0.0001f); + if (m_displayEffect == EFFECT_ZOOM) + fScale *= m_fPosZ; + // zoom image + fScale *= m_fZoomAmount; + + // calculate the resultant coordinates + for (int i = 0; i < 4; i++) + { + x[i] *= fScale * 0.5f; // as the offsets x[] and y[] are from center + y[i] *= fScale * fPixelRatio * 0.5f; + // center it + x[i] += 0.5f * fScreenWidth + fOffsetX; + y[i] += 0.5f * fScreenHeight + fOffsetY; + } + // shift if we're zooming + if (m_fZoomAmount > 1) + { + float minx = x[0]; + float maxx = x[0]; + float miny = y[0]; + float maxy = y[0]; + for (int i = 1; i < 4; i++) + { + if (x[i] < minx) minx = x[i]; + if (x[i] > maxx) maxx = x[i]; + if (y[i] < miny) miny = y[i]; + if (y[i] > maxy) maxy = y[i]; + } + float w = maxx - minx; + float h = maxy - miny; + m_bCanMoveHorizontally = (w >= fScreenWidth); + m_bCanMoveVertically = (h >= fScreenHeight); + if (w >= fScreenWidth) + { // must have no black bars + if (minx + m_fZoomLeft*w > fOffsetX) + m_fZoomLeft = (fOffsetX - minx) / w; + if (maxx + m_fZoomLeft*w < fOffsetX + fScreenWidth) + m_fZoomLeft = (fScreenWidth + fOffsetX - maxx) / w; + for (float& i : x) + i += w * m_fZoomLeft; + } + if (h >= fScreenHeight) + { // must have no black bars + if (miny + m_fZoomTop*h > fOffsetY) + m_fZoomTop = (fOffsetY - miny) / h; + if (maxy + m_fZoomTop*h < fOffsetY + fScreenHeight) + m_fZoomTop = (fScreenHeight + fOffsetY - maxy) / h; + for (float& i : y) + i += m_fZoomTop * h; + } + } + // add offset from display effects + for (int i = 0; i < 4; i++) + { + x[i] += m_fPosX * m_fWidth * fScale; + y[i] += m_fPosY * m_fHeight * fScale; + } + + UpdateVertices(m_ax, m_ay, x, y, dirtyregions); + + // now render the image in the top right corner if we're zooming + if (m_fZoomAmount == 1 || m_bIsComic) + { + const float empty[4] = {}; + UpdateVertices(m_bx, m_by, empty, empty, dirtyregions); + UpdateVertices(m_sx, m_sy, empty, empty, dirtyregions); + UpdateVertices(m_ox, m_oy, empty, empty, dirtyregions); + m_bIsDirty = false; + return; + } + + float sx[4], sy[4]; + sx[0] = -m_fWidth * co + m_fHeight * si; + sy[0] = -m_fWidth * si - m_fHeight * co; + sx[1] = m_fWidth * co + m_fHeight * si; + sy[1] = m_fWidth * si - m_fHeight * co; + sx[2] = m_fWidth * co - m_fHeight * si; + sy[2] = m_fWidth * si + m_fHeight * co; + sx[3] = -m_fWidth * co - m_fHeight * si; + sy[3] = -m_fWidth * si + m_fHeight * co; + // convert to the appropriate scale + float fSmallArea = fScreenWidth * fScreenHeight / 50; + float fSmallWidth = sqrt(fSmallArea * fAR / fPixelRatio); // fAR*height = width, so total area*far = width*width + float fSmallHeight = fSmallArea / fSmallWidth; + float fSmallX = fOffsetX + fScreenWidth * 0.95f - fSmallWidth * 0.5f; + float fSmallY = fOffsetY + fScreenHeight * 0.05f + fSmallHeight * 0.5f; + fScaleNorm = fSmallWidth / m_fWidth; + fScaleInv = fSmallWidth / m_fHeight; + fScale = si * si * (fScaleInv - fScaleNorm) + fScaleNorm; + for (int i = 0; i < 4; i++) + { + sx[i] *= fScale * 0.5f; + sy[i] *= fScale * fPixelRatio * 0.5f; + } + // calculate a black border + float bx[4]; + float by[4]; + for (int i = 0; i < 4; i++) + { + if (sx[i] > 0) + bx[i] = sx[i] + 1; + else + bx[i] = sx[i] - 1; + if (sy[i] > 0) + by[i] = sy[i] + 1; + else + by[i] = sy[i] - 1; + sx[i] += fSmallX; + sy[i] += fSmallY; + bx[i] += fSmallX; + by[i] += fSmallY; + } + + fSmallX -= fSmallWidth * 0.5f; + fSmallY -= fSmallHeight * 0.5f; + + UpdateVertices(m_bx, m_by, bx, by, dirtyregions); + UpdateVertices(m_sx, m_sy, sx, sy, dirtyregions); + + // now we must render the wireframe image of the view window + // work out the direction of the top of pic vector + float scale; + if (fabs(x[1] - x[0]) > fabs(x[3] - x[0])) + scale = (sx[1] - sx[0]) / (x[1] - x[0]); + else + scale = (sx[3] - sx[0]) / (x[3] - x[0]); + float ox[4]; + float oy[4]; + ox[0] = (fOffsetX - x[0]) * scale + sx[0]; + oy[0] = (fOffsetY - y[0]) * scale + sy[0]; + ox[1] = (fOffsetX + fScreenWidth - x[0]) * scale + sx[0]; + oy[1] = (fOffsetY - y[0]) * scale + sy[0]; + ox[2] = (fOffsetX + fScreenWidth - x[0]) * scale + sx[0]; + oy[2] = (fOffsetY + fScreenHeight - y[0]) * scale + sy[0]; + ox[3] = (fOffsetX - x[0]) * scale + sx[0]; + oy[3] = (fOffsetY + fScreenHeight - y[0]) * scale + sy[0]; + // crop to within the range of our piccy + for (int i = 0; i < 4; i++) + { + if (ox[i] < fSmallX) ox[i] = fSmallX; + if (ox[i] > fSmallX + fSmallWidth) ox[i] = fSmallX + fSmallWidth; + if (oy[i] < fSmallY) oy[i] = fSmallY; + if (oy[i] > fSmallY + fSmallHeight) oy[i] = fSmallY + fSmallHeight; + } + + UpdateVertices(m_ox, m_oy, ox, oy, dirtyregions); + m_bIsDirty = false; +} + +void CSlideShowPic::Keep() +{ + // this is called if we need to keep the current pic on screen + // to wait for the next pic to load + if (!m_bDrawNextImage) return ; // don't need to keep pic + // hold off the start of the next frame + m_transitionEnd.start = m_iCounter; +} + +bool CSlideShowPic::StartTransition() +{ + // this is called if we need to start transitioning immediately to the new picture + if (m_bDrawNextImage) return false; // don't need to do anything as we are already transitioning + // decrease the number of display frame + m_transitionEnd.start = m_iCounter; + m_bTransitionImmediately = true; + return true; +} + +void CSlideShowPic::Pause(bool bPause) +{ + if (!m_bDrawNextImage) + m_bPause = bPause; +} + +void CSlideShowPic::SetInSlideshow(bool slideshow) +{ + if (slideshow && m_displayEffect == EFFECT_NO_TIMEOUT) + m_displayEffect = EFFECT_NONE; +} + +int CSlideShowPic::GetTransitionTime(int iType) const +{ + if (iType == 0) // start transition + return m_transitionStart.length; + else // iType == 1 // end transition + return m_transitionEnd.length; +} + +void CSlideShowPic::SetTransitionTime(int iType, int iTime) +{ + if (iType == 0) // start transition + m_transitionStart.length = iTime; + else // iType == 1 // end transition + m_transitionEnd.length = iTime; +} + +void CSlideShowPic::Rotate(float fRotateAngle, bool immediate /* = false */) +{ + if (m_bDrawNextImage) return; + if (m_transitionTemp.type == TRANSITION_ZOOM) return; + if (immediate) + { + m_fAngle += fRotateAngle; + return; + } + + // if there is a rotation ongoing already + // add the new angle to the old destination angle + if (m_transitionTemp.type == TRANSITION_ROTATE && + m_transitionTemp.start + m_transitionTemp.length > m_iCounter) + { + int remainder = m_transitionTemp.start + m_transitionTemp.length - m_iCounter; + fRotateAngle += m_fTransitionAngle * remainder; + } + + m_transitionTemp.type = TRANSITION_ROTATE; + m_transitionTemp.start = m_iCounter; + m_transitionTemp.length = IMMEDIATE_TRANSITION_TIME; + m_fTransitionAngle = fRotateAngle / (float)m_transitionTemp.length; + // reset the timer + m_transitionEnd.start = m_iCounter + m_transitionStart.length + (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME)); +} + +void CSlideShowPic::Zoom(float fZoom, bool immediate /* = false */) +{ + if (m_bDrawNextImage) return; + if (m_transitionTemp.type == TRANSITION_ROTATE) return; + if (immediate) + { + m_fZoomAmount = fZoom; + return; + } + m_transitionTemp.type = TRANSITION_ZOOM; + m_transitionTemp.start = m_iCounter; + m_transitionTemp.length = IMMEDIATE_TRANSITION_TIME; + m_fTransitionZoom = (fZoom - m_fZoomAmount) / (float)m_transitionTemp.length; + // reset the timer + m_transitionEnd.start = m_iCounter + m_transitionStart.length + (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME)); + // turn off the render effects until we're back down to normal zoom + m_bNoEffect = true; +} + +void CSlideShowPic::Move(float fDeltaX, float fDeltaY) +{ + m_fZoomLeft += fDeltaX; + m_fZoomTop += fDeltaY; + // reset the timer + // m_transitionEnd.start = m_iCounter + m_transitionStart.length + (int)(CServiceBroker::GetWinSystem()->GetGfxContext().GetFPS() * CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_SLIDESHOW_STAYTIME)); +} + +void CSlideShowPic::Render() +{ + std::unique_lock<CCriticalSection> lock(m_textureAccess); + + Render(m_ax, m_ay, m_pImage.get(), (m_alpha << 24) | 0xFFFFFF); + + // now render the image in the top right corner if we're zooming + if (m_fZoomAmount == 1.0f || m_bIsComic) return ; + + Render(m_bx, m_by, NULL, PICTURE_VIEW_BOX_BACKGROUND); + Render(m_sx, m_sy, m_pImage.get(), 0xFFFFFFFF); + Render(m_ox, m_oy, NULL, PICTURE_VIEW_BOX_COLOR); +} + +#ifdef HAS_DX +bool CSlideShowPic::UpdateVertexBuffer(Vertex* vertices) +{ + if (!m_vb) // create new + { + CD3D11_BUFFER_DESC desc(sizeof(Vertex) * 5, D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE); + D3D11_SUBRESOURCE_DATA initData = {}; + initData.pSysMem = vertices; + initData.SysMemPitch = sizeof(Vertex) * 5; + if (SUCCEEDED(DX::DeviceResources::Get()->GetD3DDevice()->CreateBuffer(&desc, &initData, m_vb.ReleaseAndGetAddressOf()))) + return true; + } + else // update + { + ID3D11DeviceContext* pContext = DX::DeviceResources::Get()->GetD3DContext(); + D3D11_MAPPED_SUBRESOURCE res; + if (SUCCEEDED(pContext->Map(m_vb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &res))) + { + memcpy(res.pData, vertices, sizeof(Vertex) * 5); + pContext->Unmap(m_vb.Get(), 0); + return true; + } + } + + return false; +} +#endif + +void CSlideShowPic::Render(float* x, float* y, CTexture* pTexture, UTILS::COLOR::Color color) +{ +#ifdef HAS_DX + Vertex vertex[5]; + for (int i = 0; i < 4; i++) + { + vertex[i].pos = XMFLOAT3( x[i], y[i], 0); + CD3DHelper::XMStoreColor(&vertex[i].color, color); + vertex[i].texCoord = XMFLOAT2(0.0f, 0.0f); + vertex[i].texCoord2 = XMFLOAT2(0.0f, 0.0f); + } + + if (pTexture) + { + vertex[1].texCoord.x = vertex[2].texCoord.x = (float) pTexture->GetWidth() / pTexture->GetTextureWidth(); + vertex[2].texCoord.y = vertex[3].texCoord.y = (float) pTexture->GetHeight() / pTexture->GetTextureHeight(); + } + else + { + vertex[1].texCoord.x = vertex[2].texCoord.x = 1.0f; + vertex[2].texCoord.y = vertex[3].texCoord.y = 1.0f; + } + vertex[4] = vertex[0]; // Not used when pTexture != NULL + + CGUIShaderDX* pGUIShader = DX::Windowing()->GetGUIShader(); + pGUIShader->Begin(SHADER_METHOD_RENDER_TEXTURE_BLEND); + + // Set state to render the image + if (pTexture) + { + pTexture->LoadToGPU(); + CDXTexture* dxTexture = reinterpret_cast<CDXTexture*>(pTexture); + ID3D11ShaderResourceView* shaderRes = dxTexture->GetShaderResource(); + pGUIShader->SetShaderViews(1, &shaderRes); + pGUIShader->DrawQuad(vertex[0], vertex[1], vertex[2], vertex[3]); + } + else + { + if (!UpdateVertexBuffer(vertex)) + return; + + ComPtr<ID3D11DeviceContext> pContext = DX::DeviceResources::Get()->GetD3DContext(); + + unsigned stride = sizeof(Vertex); + unsigned offset = 0; + pContext->IASetVertexBuffers(0, 1, m_vb.GetAddressOf(), &stride, &offset); + pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP); + + pGUIShader->Draw(5, 0); + pGUIShader->RestoreBuffers(); + } + +#elif defined(HAS_GL) + CRenderSystemGL *renderSystem = dynamic_cast<CRenderSystemGL*>(CServiceBroker::GetRenderSystem()); + if (pTexture) + { + pTexture->LoadToGPU(); + pTexture->BindToUnit(0); + + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + renderSystem->EnableShader(ShaderMethodGL::SM_TEXTURE); + } + else + { + renderSystem->EnableShader(ShaderMethodGL::SM_DEFAULT); + } + + float u1 = 0, u2 = 1, v1 = 0, v2 = 1; + if (pTexture) + { + u2 = (float)pTexture->GetWidth() / pTexture->GetTextureWidth(); + v2 = (float)pTexture->GetHeight() / pTexture->GetTextureHeight(); + } + + GLubyte colour[4]; + GLubyte idx[4] = {0, 1, 3, 2}; //determines order of the vertices + GLuint vertexVBO; + GLuint indexVBO; + struct PackedVertex + { + float x, y, z; + float u1, v1; + } vertex[4]; + + // Setup vertex position values + vertex[0].x = x[0]; + vertex[0].y = y[0]; + vertex[0].z = 0; + vertex[0].u1 = u1; + vertex[0].v1 = v1; + + vertex[1].x = x[1]; + vertex[1].y = y[1]; + vertex[1].z = 0; + vertex[1].u1 = u2; + vertex[1].v1 = v1; + + vertex[2].x = x[2]; + vertex[2].y = y[2]; + vertex[2].z = 0; + vertex[2].u1 = u2; + vertex[2].v1 = v2; + + vertex[3].x = x[3]; + vertex[3].y = y[3]; + vertex[3].z = 0; + vertex[3].u1 = u1; + vertex[3].v1 = v2; + + GLint posLoc = renderSystem->ShaderGetPos(); + GLint tex0Loc = renderSystem->ShaderGetCoord0(); + GLint uniColLoc= renderSystem->ShaderGetUniCol(); + + glGenBuffers(1, &vertexVBO); + glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(PackedVertex)*4, &vertex[0], GL_STATIC_DRAW); + + glVertexAttribPointer(posLoc, 3, GL_FLOAT, 0, sizeof(PackedVertex), + reinterpret_cast<const GLvoid*>(offsetof(PackedVertex, x))); + glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, 0, sizeof(PackedVertex), + reinterpret_cast<const GLvoid*>(offsetof(PackedVertex, u1))); + + glEnableVertexAttribArray(posLoc); + glEnableVertexAttribArray(tex0Loc); + + // Setup Colour values + colour[0] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::R, color); + colour[1] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::G, color); + colour[2] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::B, color); + colour[3] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::A, color); + + glUniform4f(uniColLoc,(colour[0] / 255.0f), (colour[1] / 255.0f), + (colour[2] / 255.0f), (colour[3] / 255.0f)); + + glGenBuffers(1, &indexVBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLubyte)*4, idx, GL_STATIC_DRAW); + + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, 0); + + glDisableVertexAttribArray(posLoc); + glDisableVertexAttribArray(tex0Loc); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &vertexVBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &indexVBO); + + renderSystem->DisableShader(); + +#elif defined(HAS_GLES) + CRenderSystemGLES *renderSystem = dynamic_cast<CRenderSystemGLES*>(CServiceBroker::GetRenderSystem()); + if (pTexture) + { + pTexture->LoadToGPU(); + pTexture->BindToUnit(0); + + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); // Turn Blending On + + renderSystem->EnableGUIShader(ShaderMethodGLES::SM_TEXTURE); + } + else + { + renderSystem->EnableGUIShader(ShaderMethodGLES::SM_DEFAULT); + } + + float u1 = 0, u2 = 1, v1 = 0, v2 = 1; + if (pTexture) + { + u2 = (float)pTexture->GetWidth() / pTexture->GetTextureWidth(); + v2 = (float)pTexture->GetHeight() / pTexture->GetTextureHeight(); + } + + GLubyte col[4]; + GLfloat ver[4][3]; + GLfloat tex[4][2]; + GLubyte idx[4] = {0, 1, 3, 2}; //determines order of triangle strip + + GLint posLoc = renderSystem->GUIShaderGetPos(); + GLint tex0Loc = renderSystem->GUIShaderGetCoord0(); + GLint uniColLoc= renderSystem->GUIShaderGetUniCol(); + + glVertexAttribPointer(posLoc, 3, GL_FLOAT, 0, 0, ver); + glVertexAttribPointer(tex0Loc, 2, GL_FLOAT, 0, 0, tex); + + glEnableVertexAttribArray(posLoc); + glEnableVertexAttribArray(tex0Loc); + + // Setup Colour values + col[0] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::R, color); + col[1] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::G, color); + col[2] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::B, color); + col[3] = KODI::UTILS::GL::GetChannelFromARGB(KODI::UTILS::GL::ColorChannel::A, color); + + if (CServiceBroker::GetWinSystem()->UseLimitedColor()) + { + col[0] = (235 - 16) * col[0] / 255 + 16; + col[1] = (235 - 16) * col[1] / 255 + 16; + col[2] = (235 - 16) * col[2] / 255 + 16; + } + + for (int i=0; i<4; i++) + { + // Setup vertex position values + ver[i][0] = x[i]; + ver[i][1] = y[i]; + ver[i][2] = 0.0f; + } + // Setup texture coordinates + tex[0][0] = tex[3][0] = u1; + tex[0][1] = tex[1][1] = v1; + tex[1][0] = tex[2][0] = u2; + tex[2][1] = tex[3][1] = v2; + + glUniform4f(uniColLoc,(col[0] / 255.0f), (col[1] / 255.0f), (col[2] / 255.0f), (col[3] / 255.0f)); + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx); + + glDisableVertexAttribArray(posLoc); + glDisableVertexAttribArray(tex0Loc); + + renderSystem->DisableGUIShader(); + +#endif +} |