/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include #include #include #include #include #include #include #include #include #undef IN #undef OUT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "TransitionImpl.hxx" #if OSL_DEBUG_LEVEL > 0 #include #endif using namespace ::com::sun::star; using ::com::sun::star::beans::XFastPropertySet; using ::com::sun::star::uno::Any; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::UNO_QUERY_THROW; namespace { typedef cppu::WeakComponentImplHelper OGLTransitionerImplBase; #if OSL_DEBUG_LEVEL > 0 class TimerContext { public: explicit TimerContext(OUString aWhat) : m_aWhat(std::move(aWhat)) , m_StartTime(std::chrono::steady_clock::now()) { } ~TimerContext() { auto const aDuration(std::chrono::steady_clock::now() - m_StartTime); SAL_INFO("slideshow.opengl", m_aWhat << " took: " << std::chrono::duration_cast(aDuration).count()); } private: OUString const m_aWhat; std::chrono::steady_clock::time_point const m_StartTime; }; #endif struct OGLFormat { GLint nInternalFormat; GLenum eFormat; GLenum eType; }; /* channel ordering: (0:rgba, 1:bgra, 2:argb, 3:abgr) */ int calcComponentOrderIndex(const uno::Sequence& rTags) { using namespace rendering::ColorComponentTag; static const sal_Int8 aOrderTable[] = { RGB_RED, RGB_GREEN, RGB_BLUE, ALPHA, RGB_BLUE, RGB_GREEN, RGB_RED, ALPHA, ALPHA, RGB_RED, RGB_GREEN, RGB_BLUE, ALPHA, RGB_BLUE, RGB_GREEN, RGB_RED, }; const sal_Int32 nNumComps(rTags.getLength()); const sal_Int8* pLine=aOrderTable; for(int i=0; i<4; ++i) { int j=0; while( j<4 && j& pOGLTransition ); bool initialize( const Reference< presentation::XSlideShowView >& xView, const Reference< rendering::XBitmap >& xLeavingSlide, const Reference< rendering::XBitmap >& xEnteringSlide ); // XTransition virtual void SAL_CALL update( double nTime ) override; virtual void SAL_CALL viewChanged( const Reference< presentation::XSlideShowView >& rView, const Reference< rendering::XBitmap >& rLeavingBitmap, const Reference< rendering::XBitmap >& rEnteringBitmap ) override; protected: void disposeTextures(); // WeakComponentImplHelperBase virtual void SAL_CALL disposing() override; bool isDisposed() const { return (rBHelper.bDisposed || rBHelper.bInDispose); } void createTexture( GLuint* texID, bool useMipmap, const uno::Sequence& data, const OGLFormat* pFormat ); const OGLFormat* chooseFormats(); private: void impl_initializeFlags( bool const bGLXPresent ); void impl_dispose(); void setSlides( const Reference< rendering::XBitmap >& xLeavingSlide , const uno::Reference< rendering::XBitmap >& xEnteringSlide ); void impl_prepareSlides(); void impl_createTexture( bool useMipmap, const uno::Sequence& data, const OGLFormat* pFormat ); bool initWindowFromSlideShowView( const uno::Reference< presentation::XSlideShowView >& xView ); /** After the window has been created, and the slides have been set, we'll initialize the slides with OpenGL. */ void GLInitSlides(); bool impl_prepareTransition(); private: rtl::Reference mpContext; /** OpenGL handle to the leaving slide's texture */ GLuint maLeavingSlideGL; /** OpenGL handle to the entering slide's texture */ GLuint maEnteringSlideGL; Reference< presentation::XSlideShowView > mxView; Reference< rendering::XIntegerBitmap > mxLeavingBitmap; Reference< rendering::XIntegerBitmap > mxEnteringBitmap; /** raw bytes of the entering bitmap */ uno::Sequence maEnteringBytes; /** raw bytes of the leaving bitmap */ uno::Sequence maLeavingBytes; bool mbRestoreSync; /** the form the raw bytes are in for the bitmaps */ rendering::IntegerBitmapLayout maSlideBitmapLayout; /** the size of the slides */ geometry::IntegerSize2D maSlideSize; /** Our Transition to be used. */ std::shared_ptr mpTransition; public: /** whether we are running on ATI fglrx with bug related to textures */ bool mbBrokenTexturesATI; /** GL version */ float mnGLVersion; /** Whether the display has GLX extension on X11, always true otherwise (?) */ bool mbValidOpenGLContext; #if OSL_DEBUG_LEVEL > 0 std::chrono::steady_clock::time_point m_UpdateStartTime; std::chrono::steady_clock::time_point m_UpdateEndTime; std::chrono::steady_clock::time_point m_StartTime; std::chrono::steady_clock::time_point m_EndTime; std::chrono::steady_clock::duration m_TotalUpdateDuration; int mnFrameCount; #endif }; bool OGLTransitionerImpl::initialize( const Reference< presentation::XSlideShowView >& xView, const Reference< rendering::XBitmap >& xLeavingSlide, const Reference< rendering::XBitmap >& xEnteringSlide ) { bool const bValidContext( initWindowFromSlideShowView( xView ) ); impl_initializeFlags( bValidContext ); setSlides( xLeavingSlide, xEnteringSlide ); return mbValidOpenGLContext; } void OGLTransitionerImpl::impl_initializeFlags( bool const bValidContext ) { mbValidOpenGLContext = bValidContext; if ( bValidContext ) { CHECK_GL_ERROR(); mnGLVersion = OpenGLHelper::getGLVersion(); SAL_INFO("slideshow.opengl", "GL version: " << mnGLVersion << "" ); #if defined( UNX ) && !defined( MACOSX ) const GLubyte* vendor = glGetString( GL_VENDOR ); /* TODO: check for version once the bug in fglrx driver is fixed */ mbBrokenTexturesATI = (vendor && strcmp( reinterpret_cast(vendor), "ATI Technologies Inc." ) == 0 ); #endif CHECK_GL_ERROR(); } } bool OGLTransitionerImpl::initWindowFromSlideShowView( const Reference< presentation::XSlideShowView >& xView ) { osl::MutexGuard const guard( m_aMutex ); if (isDisposed()) return false; mxView = xView; if( !mxView.is() ) return false; #if OSL_DEBUG_LEVEL > 0 TimerContext aTimerContext("initWindowFromSlideShowView"); #endif /// take the XSlideShowView and extract the parent window from it. see viewmediashape.cxx uno::Reference< rendering::XCanvas > xCanvas(mxView->getCanvas(), uno::UNO_QUERY_THROW); uno::Sequence< uno::Any > aDeviceParams; ::canvas::tools::getDeviceInfo( xCanvas, aDeviceParams ); OUString aImplName; aDeviceParams[ 0 ] >>= aImplName; sal_Int64 aVal = 0; aDeviceParams[1] >>= aVal; mpContext = OpenGLContext::Create(); OutputDevice* pDevice = reinterpret_cast(aVal); vcl::Window* pWindow = pDevice ? pDevice->GetOwnerWindow() : nullptr; if( !mpContext->init( pWindow) ) { mpContext->requestLegacyContext(); if( !mpContext->init( pWindow ) ) return false; } SAL_INFO("slideshow.opengl", "created the context"); mpContext->makeCurrent(); CHECK_GL_ERROR(); awt::Rectangle aCanvasArea = mxView->getCanvasArea(); mpContext->setWinPosAndSize(Point(aCanvasArea.X, aCanvasArea.Y), Size(aCanvasArea.Width, aCanvasArea.Height)); SAL_INFO("slideshow.opengl", "canvas area: " << aCanvasArea.X << "," << aCanvasArea.Y << " - " << aCanvasArea.Width << "x" << aCanvasArea.Height); CHECK_GL_ERROR(); glEnable(GL_CULL_FACE); CHECK_GL_ERROR(); glCullFace(GL_BACK); CHECK_GL_ERROR(); glClearColor (0, 0, 0, 0); CHECK_GL_ERROR(); glClear(GL_COLOR_BUFFER_BIT); CHECK_GL_ERROR(); mpContext->swapBuffers(); CHECK_GL_ERROR(); return true; } void OGLTransitionerImpl::setSlides( const uno::Reference< rendering::XBitmap >& xLeavingSlide, const uno::Reference< rendering::XBitmap >& xEnteringSlide ) { osl::MutexGuard const guard( m_aMutex ); if (isDisposed()) return; mxLeavingBitmap.set( xLeavingSlide , UNO_QUERY_THROW ); mxEnteringBitmap.set( xEnteringSlide , UNO_QUERY_THROW ); maSlideSize = mxLeavingBitmap->getSize(); SAL_INFO("slideshow.opengl", "leaving bitmap area: " << maSlideSize.Width << "x" << maSlideSize.Height); maSlideSize = mxEnteringBitmap->getSize(); SAL_INFO("slideshow.opengl", "entering bitmap area: " << maSlideSize.Width << "x" << maSlideSize.Height); //to avoid annoying flashing under X entering and leaving slides with opengl effects set the leaving //bitmap as the background pixmap of the opengl child window and the entering bitmap as the background //pixmap of the non-opengl parent window. If any expose events occur around the start and end of //the transition then those windows are default filled by X with the desired start/end image so there's //no visible flash SystemChildWindow* pChildWindow = mpContext->getChildWindow(); if (!pChildWindow) return; css::uno::Reference xEnteringFastPropertySet(mxEnteringBitmap, css::uno::UNO_QUERY); css::uno::Reference xLeavingFastPropertySet(mxLeavingBitmap, css::uno::UNO_QUERY); css::uno::Sequence aEnteringBitmap; css::uno::Sequence aLeavingBitmap; if (xEnteringFastPropertySet && xLeavingFastPropertySet) { xEnteringFastPropertySet->getFastPropertyValue(1) >>= aEnteringBitmap; xLeavingFastPropertySet->getFastPropertyValue(1) >>= aLeavingBitmap; } if (aEnteringBitmap.getLength() == 2 && aLeavingBitmap.getLength() == 2) pChildWindow->SetLeaveEnterBackgrounds(aLeavingBitmap, aEnteringBitmap); } void OGLTransitionerImpl::impl_prepareSlides() { geometry::IntegerRectangle2D aSlideRect; aSlideRect.X1 = 0; aSlideRect.X2 = maSlideSize.Width; aSlideRect.Y1 = 0; aSlideRect.Y2 = maSlideSize.Height; CHECK_GL_ERROR(); mpContext->sync(); CHECK_GL_ERROR(); maLeavingBytes = mxLeavingBitmap->getData(maSlideBitmapLayout, aSlideRect); maEnteringBytes = mxEnteringBitmap->getData(maSlideBitmapLayout, aSlideRect); CHECK_GL_ERROR(); GLInitSlides(); SAL_WARN_IF(maSlideBitmapLayout.PlaneStride != 0, "slideshow.opengl","only handle no plane stride now"); mpContext->sync(); CHECK_GL_ERROR(); // synchronized X still gives us much smoother play // I suspect some issues in above code in slideshow // synchronize whole transition for now const GLWindow& rGLWindow(mpContext->getOpenGLWindow()); mbRestoreSync = rGLWindow.Synchronize(true); } bool OGLTransitionerImpl::impl_prepareTransition() { if( mpTransition && mpTransition->getSettings().mnRequiredGLVersion <= mnGLVersion ) return mpTransition->prepare( maLeavingSlideGL, maEnteringSlideGL, mpContext.get() ); return false; } bool OGLTransitionerImpl::setTransition( const std::shared_ptr& pTransition ) { if ( mpTransition ) // already initialized return true; mpTransition = pTransition; mpContext->makeCurrent(); CHECK_GL_ERROR(); bool succeeded = impl_prepareTransition(); if (!succeeded) { mpTransition = nullptr; return false; } impl_prepareSlides(); // tdf#91456: When the OpenGL context is initialized but nothing has been rendered on it // it can happen, that an "empty" screen is drawn. Therefore, drawing the content of time 0 // onto the context update(0); return true; } void OGLTransitionerImpl::createTexture( GLuint* texID, bool useMipmap, const uno::Sequence& data, const OGLFormat* pFormat ) { CHECK_GL_ERROR(); glDeleteTextures( 1, texID ); glGenTextures( 1, texID ); glBindTexture( GL_TEXTURE_2D, *texID ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); CHECK_GL_ERROR(); impl_createTexture( useMipmap, data, pFormat ); SAL_WARN_IF(!glIsTexture(*texID), "slideshow.opengl", "Can't generate Leaving slide textures in OpenGL"); CHECK_GL_ERROR(); } class OGLColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace > { private: uno::Sequence< sal_Int8 > maComponentTags; uno::Sequence< sal_Int32 > maBitCounts; virtual sal_Int8 SAL_CALL getType( ) override { return rendering::ColorSpaceType::RGB; } virtual uno::Sequence< sal_Int8 > SAL_CALL getComponentTags( ) override { return maComponentTags; } virtual sal_Int8 SAL_CALL getRenderingIntent( ) override { return rendering::RenderingIntent::PERCEPTUAL; } virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override { return uno::Sequence< beans::PropertyValue >(); } virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor, const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override { // TODO(P3): if we know anything about target // colorspace, this can be greatly sped up uno::Sequence aIntermediate( convertToARGB(deviceColor)); return targetColorSpace->convertFromARGB(aIntermediate); } virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override { const double* pIn( deviceColor.getConstArray() ); const std::size_t nLen( deviceColor.getLength() ); ENSURE_ARG_OR_THROW2(nLen%4==0, "number of channels no multiple of 4", static_cast(this), 0); uno::Sequence< rendering::RGBColor > aRes(nLen/4); rendering::RGBColor* pOut( aRes.getArray() ); for( std::size_t i=0; i SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override { const double* pIn( deviceColor.getConstArray() ); const std::size_t nLen( deviceColor.getLength() ); ENSURE_ARG_OR_THROW2(nLen%4==0, "number of channels no multiple of 4", static_cast(this), 0); uno::Sequence< rendering::ARGBColor > aRes(nLen/4); rendering::ARGBColor* pOut( aRes.getArray() ); for( std::size_t i=0; i SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override { const double* pIn( deviceColor.getConstArray() ); const std::size_t nLen( deviceColor.getLength() ); ENSURE_ARG_OR_THROW2(nLen%4==0, "number of channels no multiple of 4", static_cast(this), 0); uno::Sequence< rendering::ARGBColor > aRes(nLen/4); rendering::ARGBColor* pOut( aRes.getArray() ); for( std::size_t i=0; i SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override { const sal_Int32 nLen( rgbColor.getLength() ); uno::Sequence< double > aRes(nLen*4); double* pColors=aRes.getArray(); for( const rendering::RGBColor& rIn : rgbColor ) { *pColors++ = rIn.Red; *pColors++ = rIn.Green; *pColors++ = rIn.Blue; *pColors++ = 1.0; } return aRes; } virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override { const sal_Int32 nLen( rgbColor.getLength() ); uno::Sequence< double > aRes(nLen*4); double* pColors=aRes.getArray(); for( const rendering::ARGBColor& rIn : rgbColor ) { *pColors++ = rIn.Red; *pColors++ = rIn.Green; *pColors++ = rIn.Blue; *pColors++ = rIn.Alpha; } return aRes; } virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override { const sal_Int32 nLen( rgbColor.getLength() ); uno::Sequence< double > aRes(nLen*4); double* pColors=aRes.getArray(); for( const rendering::ARGBColor& rIn : rgbColor ) { *pColors++ = rIn.Red/rIn.Alpha; *pColors++ = rIn.Green/rIn.Alpha; *pColors++ = rIn.Blue/rIn.Alpha; *pColors++ = rIn.Alpha; } return aRes; } // XIntegerBitmapColorSpace virtual sal_Int32 SAL_CALL getBitsPerPixel( ) override { return 32; } virtual uno::Sequence< sal_Int32 > SAL_CALL getComponentBitCounts( ) override { return maBitCounts; } virtual sal_Int8 SAL_CALL getEndianness( ) override { return util::Endianness::LITTLE; } virtual uno::Sequence SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< sal_Int8 >& deviceColor, const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override { if( dynamic_cast(targetColorSpace.get()) ) { const sal_Int32 nLen( deviceColor.getLength() ); ENSURE_ARG_OR_THROW2(nLen%4==0, "number of channels no multiple of 4", static_cast(this), 0); uno::Sequence aRes(nLen); std::transform(deviceColor.begin(), deviceColor.end(), aRes.getArray(), vcl::unotools::toDoubleColor); return aRes; } else { // TODO(P3): if we know anything about target // colorspace, this can be greatly sped up uno::Sequence aIntermediate( convertIntegerToARGB(deviceColor)); return targetColorSpace->convertFromARGB(aIntermediate); } } virtual uno::Sequence< sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< sal_Int8 >& deviceColor, const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override { if( dynamic_cast(targetColorSpace.get()) ) { // it's us, so simply pass-through the data return deviceColor; } else { // TODO(P3): if we know anything about target // colorspace, this can be greatly sped up uno::Sequence aIntermediate( convertIntegerToARGB(deviceColor)); return targetColorSpace->convertIntegerFromARGB(aIntermediate); } } virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< sal_Int8 >& deviceColor ) override { const sal_Int8* pIn( deviceColor.getConstArray() ); const std::size_t nLen( deviceColor.getLength() ); ENSURE_ARG_OR_THROW2(nLen%4==0, "number of channels no multiple of 4", static_cast(this), 0); uno::Sequence< rendering::RGBColor > aRes(nLen/4); rendering::RGBColor* pOut( aRes.getArray() ); for( std::size_t i=0; i SAL_CALL convertIntegerToARGB( const uno::Sequence< sal_Int8 >& deviceColor ) override { const sal_Int8* pIn( deviceColor.getConstArray() ); const std::size_t nLen( deviceColor.getLength() ); ENSURE_ARG_OR_THROW2(nLen%4==0, "number of channels no multiple of 4", static_cast(this), 0); uno::Sequence< rendering::ARGBColor > aRes(nLen/4); rendering::ARGBColor* pOut( aRes.getArray() ); for( std::size_t i=0; i SAL_CALL convertIntegerToPARGB( const uno::Sequence< sal_Int8 >& deviceColor ) override { const sal_Int8* pIn( deviceColor.getConstArray() ); const std::size_t nLen( deviceColor.getLength() ); ENSURE_ARG_OR_THROW2(nLen%4==0, "number of channels no multiple of 4", static_cast(this), 0); uno::Sequence< rendering::ARGBColor > aRes(nLen/4); rendering::ARGBColor* pOut( aRes.getArray() ); for( std::size_t i=0; i SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override { const sal_Int32 nLen( rgbColor.getLength() ); uno::Sequence< sal_Int8 > aRes(nLen*4); sal_Int8* pColors=aRes.getArray(); for( const rendering::RGBColor& rIn : rgbColor ) { *pColors++ = vcl::unotools::toByteColor(rIn.Red); *pColors++ = vcl::unotools::toByteColor(rIn.Green); *pColors++ = vcl::unotools::toByteColor(rIn.Blue); *pColors++ = -1; } return aRes; } virtual uno::Sequence< sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override { const sal_Int32 nLen( rgbColor.getLength() ); uno::Sequence< sal_Int8 > aRes(nLen*4); sal_Int8* pColors=aRes.getArray(); for( const rendering::ARGBColor& rIn : rgbColor ) { *pColors++ = vcl::unotools::toByteColor(rIn.Red); *pColors++ = vcl::unotools::toByteColor(rIn.Green); *pColors++ = vcl::unotools::toByteColor(rIn.Blue); *pColors++ = vcl::unotools::toByteColor(rIn.Alpha); } return aRes; } virtual uno::Sequence< sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override { const sal_Int32 nLen( rgbColor.getLength() ); uno::Sequence< sal_Int8 > aRes(nLen*4); sal_Int8* pColors=aRes.getArray(); for( const rendering::ARGBColor& rIn : rgbColor ) { *pColors++ = vcl::unotools::toByteColor(rIn.Red/rIn.Alpha); *pColors++ = vcl::unotools::toByteColor(rIn.Green/rIn.Alpha); *pColors++ = vcl::unotools::toByteColor(rIn.Blue/rIn.Alpha); *pColors++ = vcl::unotools::toByteColor(rIn.Alpha); } return aRes; } public: OGLColorSpace() : maComponentTags(4), maBitCounts(4) { sal_Int8* pTags = maComponentTags.getArray(); sal_Int32* pBitCounts = maBitCounts.getArray(); pTags[0] = rendering::ColorComponentTag::RGB_RED; pTags[1] = rendering::ColorComponentTag::RGB_GREEN; pTags[2] = rendering::ColorComponentTag::RGB_BLUE; pTags[3] = rendering::ColorComponentTag::ALPHA; pBitCounts[0] = pBitCounts[1] = pBitCounts[2] = pBitCounts[3] = 8; } }; uno::Reference const & getOGLColorSpace() { static uno::Reference theSpace = new OGLColorSpace(); return theSpace; } void buildMipmaps( GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * data) { if (epoxy_has_gl_extension("GL_ARB_framebuffer_object")) { glTexImage2D( GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); glGenerateMipmap(GL_TEXTURE_2D); } else { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexImage2D( GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } void OGLTransitionerImpl::impl_createTexture( bool useMipmap, const uno::Sequence& data, const OGLFormat* pFormat ) { if( !pFormat ) { CHECK_GL_ERROR(); // force-convert color to ARGB8888 int color space uno::Sequence tempBytes( maSlideBitmapLayout.ColorSpace->convertToIntegerColorSpace( data, getOGLColorSpace())); buildMipmaps( GL_RGBA, maSlideSize.Width, maSlideSize.Height, GL_RGBA, GL_UNSIGNED_BYTE, &tempBytes[0]); if (epoxy_has_gl_extension("GL_EXT_texture_filter_anisotropic")) { //anistropic filtering (to make texturing not suck when looking at polygons from oblique angles) GLfloat largest_supported_anisotropy; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy); } } else { if( mpTransition && !mbBrokenTexturesATI && !useMipmap) { glTexImage2D( GL_TEXTURE_2D, 0, pFormat->nInternalFormat, maSlideSize.Width, maSlideSize.Height, 0, pFormat->eFormat, pFormat->eType, &data[0] ); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); } else { buildMipmaps( pFormat->nInternalFormat, maSlideSize.Width, maSlideSize.Height, pFormat->eFormat, pFormat->eType, &data[0] ); if (epoxy_has_gl_extension("GL_EXT_texture_filter_anisotropic")) { //anistropic filtering (to make texturing not suck when looking at polygons from oblique angles) GLfloat largest_supported_anisotropy; glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy ); } } } CHECK_GL_ERROR(); } const OGLFormat* OGLTransitionerImpl::chooseFormats() { const OGLFormat* pDetectedFormat=nullptr; uno::Reference xIntColorSpace( maSlideBitmapLayout.ColorSpace); if( xIntColorSpace->getType() == rendering::ColorSpaceType::RGB || xIntColorSpace->getType() == rendering::ColorSpaceType::SRGB ) { /* table for canvas->OGL format mapping. outer index is number of color components (0:3, 1:4), then comes bits per pixel (0:16, 1:24, 2:32), then channel ordering: (0:rgba, 1:bgra, 2:argb, 3:abgr) */ static const OGLFormat lcl_RGB24[] = { // 24 bit RGB {3, GL_BGR, GL_UNSIGNED_BYTE}, {3, GL_RGB, GL_UNSIGNED_BYTE}, {3, GL_BGR, GL_UNSIGNED_BYTE}, {3, GL_RGB, GL_UNSIGNED_BYTE} }; #if defined(GL_VERSION_1_2) && defined(GLU_VERSION_1_3) // more format constants available static const OGLFormat lcl_RGB16[] = { // 16 bit RGB {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, {3, GL_RGB, GL_UNSIGNED_SHORT_5_6_5} }; static const OGLFormat lcl_ARGB16_4[] = { // 16 bit ARGB {4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, {4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, {4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4}, {4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4} }; static const OGLFormat lcl_ARGB16_5[] = { // 16 bit ARGB {4, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, {4, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, {4, GL_BGRA, GL_UNSIGNED_SHORT_5_5_5_1}, {4, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1} }; static const OGLFormat lcl_ARGB32[] = { // 32 bit ARGB {4, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, {4, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV}, {4, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8}, {4, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8} }; const uno::Sequence aComponentTags( xIntColorSpace->getComponentTags()); const uno::Sequence aComponentBitcounts( xIntColorSpace->getComponentBitCounts()); const sal_Int32 nNumComponents( aComponentBitcounts.getLength() ); const sal_Int32 nBitsPerPixel( xIntColorSpace->getBitsPerPixel() ); // supported component ordering? const int nComponentOrderIndex( calcComponentOrderIndex(aComponentTags)); if( nComponentOrderIndex != -1 ) { switch( nBitsPerPixel ) { case 16: if( nNumComponents == 3 ) { pDetectedFormat = &lcl_RGB16[nComponentOrderIndex]; } else if( nNumComponents == 4 ) { if( aComponentBitcounts[1] == 4 ) { pDetectedFormat = &lcl_ARGB16_4[nComponentOrderIndex]; } else if( aComponentBitcounts[1] == 5 ) { pDetectedFormat = &lcl_ARGB16_5[nComponentOrderIndex]; } } break; case 24: if( nNumComponents == 3 ) { pDetectedFormat = &lcl_RGB24[nComponentOrderIndex]; } break; case 32: if ( nNumComponents == 4 ) { pDetectedFormat = &lcl_ARGB32[nComponentOrderIndex]; } break; } } #else const uno::Sequence aComponentTags( xIntColorSpace->getComponentTags()); const int nComponentOrderIndex(calcComponentOrderIndex(aComponentTags)); if( aComponentTags.getLength() == 3 && nComponentOrderIndex != -1 && xIntColorSpace->getBitsPerPixel() == 24 ) { pDetectedFormat = &lcl_RGB24[nComponentOrderIndex]; } #endif } return pDetectedFormat; } void OGLTransitionerImpl::GLInitSlides() { osl::MutexGuard const guard( m_aMutex ); if (isDisposed() || !mpTransition || mpTransition->getSettings().mnRequiredGLVersion > mnGLVersion) return; #if OSL_DEBUG_LEVEL > 0 TimerContext aTimerContext("texture creation"); #endif mpContext->makeCurrent(); const OGLFormat* pFormat = chooseFormats(); CHECK_GL_ERROR(); createTexture( &maLeavingSlideGL, mpTransition->getSettings().mbUseMipMapLeaving, maLeavingBytes, pFormat ); createTexture( &maEnteringSlideGL, mpTransition->getSettings().mbUseMipMapEntering, maEnteringBytes, pFormat ); CHECK_GL_ERROR(); mpContext->sync(); CHECK_GL_ERROR(); } void SAL_CALL OGLTransitionerImpl::update( double nTime ) { #if OSL_DEBUG_LEVEL > 0 mnFrameCount ++; m_UpdateStartTime = std::chrono::steady_clock::now(); if( mnFrameCount == 1 ) { m_StartTime = m_UpdateStartTime; m_TotalUpdateDuration = std::chrono::seconds(0); } #endif osl::MutexGuard const guard( m_aMutex ); if (isDisposed() || !mbValidOpenGLContext || !mpTransition || mpTransition->getSettings().mnRequiredGLVersion > mnGLVersion) return; mpContext->makeCurrent(); CHECK_GL_ERROR(); glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); CHECK_GL_ERROR(); const GLWindow& rGLWindow(mpContext->getOpenGLWindow()); mpTransition->display(nTime, maLeavingSlideGL, maEnteringSlideGL, maSlideSize.Width, maSlideSize.Height, static_cast(rGLWindow.Width), static_cast(rGLWindow.Height), mpContext.get()); mpContext->swapBuffers(); mpContext->show(); mpContext->sync(); CHECK_GL_ERROR(); #if OSL_DEBUG_LEVEL > 0 m_UpdateEndTime = std::chrono::steady_clock::now(); SAL_INFO("slideshow.opengl", "update time: " << nTime); SAL_INFO("slideshow.opengl", "update took: " << std::chrono::duration_cast(m_UpdateEndTime - m_UpdateStartTime).count()); m_TotalUpdateDuration += m_UpdateEndTime - m_UpdateStartTime; #endif } void SAL_CALL OGLTransitionerImpl::viewChanged( const Reference< presentation::XSlideShowView >& rView, const Reference< rendering::XBitmap >& rLeavingBitmap, const Reference< rendering::XBitmap >& rEnteringBitmap ) { SAL_INFO("slideshow.opengl", "transitioner: view changed"); impl_dispose(); initWindowFromSlideShowView( rView ); setSlides( rLeavingBitmap, rEnteringBitmap ); impl_prepareSlides(); impl_prepareTransition(); } void OGLTransitionerImpl::disposeTextures() { if (!mbValidOpenGLContext) return; mpContext->makeCurrent(); CHECK_GL_ERROR(); glDeleteTextures(1,&maLeavingSlideGL); maLeavingSlideGL = 0; glDeleteTextures(1,&maEnteringSlideGL); maEnteringSlideGL = 0; CHECK_GL_ERROR(); } void OGLTransitionerImpl::impl_dispose() { if (mbValidOpenGLContext) { mpContext->makeCurrent(); CHECK_GL_ERROR(); } if( mpTransition && mpTransition->getSettings().mnRequiredGLVersion <= mnGLVersion ) mpTransition->finish(); disposeTextures(); if( mpContext.is() ) mpContext->dispose(); mpContext.clear(); } // we are about to be disposed (someone call dispose() on us) void OGLTransitionerImpl::disposing() { osl::MutexGuard const guard( m_aMutex ); #if OSL_DEBUG_LEVEL > 0 SAL_INFO("slideshow.opengl", "dispose " << this); if( mnFrameCount ) { m_EndTime = std::chrono::steady_clock::now(); auto const duration = m_EndTime - m_StartTime; SAL_INFO("slideshow.opengl", "whole transition (frames: " << mnFrameCount << ") took: " << std::chrono::duration_cast(duration).count() << " fps: " << ((static_cast(mnFrameCount)*1000000000.0)/std::chrono::duration_cast(duration).count()) << " time spent in updates: " << std::chrono::duration_cast(m_TotalUpdateDuration).count() << " percentage of transition time: " << (100*((static_cast(std::chrono::duration_cast(m_TotalUpdateDuration).count()))/(static_cast(std::chrono::duration_cast(duration).count())))) << '%' ); } #endif if (mbRestoreSync && mpContext.is()) { // try to reestablish synchronize state const char* sal_synchronize = getenv("SAL_SYNCHRONIZE"); mpContext->getOpenGLWindow().Synchronize(sal_synchronize && *sal_synchronize == '1' ); } impl_dispose(); mpTransition.reset(); mxLeavingBitmap.clear(); mxEnteringBitmap.clear(); mxView.clear(); } OGLTransitionerImpl::OGLTransitionerImpl() : OGLTransitionerImplBase(m_aMutex) , mpContext() , maLeavingSlideGL(0) , maEnteringSlideGL(0) , mxView() , maEnteringBytes() , maLeavingBytes() , mbRestoreSync(false) , maSlideBitmapLayout() , maSlideSize() , mbBrokenTexturesATI(false) , mnGLVersion(0) , mbValidOpenGLContext(false) #if OSL_DEBUG_LEVEL > 0 , mnFrameCount(0) #endif { } typedef cppu::WeakComponentImplHelper OGLTransitionFactoryImplBase; class OGLTransitionFactoryImpl : private cppu::BaseMutex, public OGLTransitionFactoryImplBase { public: explicit OGLTransitionFactoryImpl() : OGLTransitionFactoryImplBase(m_aMutex) {} // XServiceInfo virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override { return { "com.sun.star.presentation.TransitionFactory" }; } virtual OUString SAL_CALL getImplementationName() override { return "com.sun.star.comp.presentation.OGLTransitionFactory"; } virtual sal_Bool SAL_CALL supportsService(const OUString& aServiceName) override { return cppu::supportsService(this, aServiceName); } // XTransitionFactory virtual sal_Bool SAL_CALL hasTransition( sal_Int16 transitionType, sal_Int16 transitionSubType ) override { if( !OpenGLHelper::supportsOpenGL()) return false; // A set of css::animation::TransitionSubType that don't have any meaning (in the SMIL 2.0 // standard) for MISCSHAPEWIPE have been chosen to refer to some of these "fancy" optional // transitions. (The only subtypes of 'miscShapeWipe' defined in the standard are 'heart' // and 'keyhole'.) The set of subtypes used seems to be a bit random; it starts from the // beginning of the list (in the order (numeric) in our TransitionSubType set of constants) // but then jumps a bit randomly. The numeric values as such have no meaning, but still. if( transitionType == animations::TransitionType::MISCSHAPEWIPE ) { switch( transitionSubType ) { case animations::TransitionSubType::LEFTTORIGHT: // 1 case animations::TransitionSubType::TOPTOBOTTOM: // 2 case animations::TransitionSubType::TOPLEFT: // 3 case animations::TransitionSubType::TOPRIGHT: // 4 case animations::TransitionSubType::BOTTOMRIGHT: // 5 case animations::TransitionSubType::BOTTOMLEFT: // 6 case animations::TransitionSubType::TOPCENTER: // 7 case animations::TransitionSubType::RIGHTCENTER: // 8 case animations::TransitionSubType::BOTTOMCENTER: // 9 case animations::TransitionSubType::CORNERSIN: // 11 case animations::TransitionSubType::CORNERSOUT: // 12 case animations::TransitionSubType::VERTICAL: // 13 case animations::TransitionSubType::HORIZONTAL: // 14 case animations::TransitionSubType::DIAMOND: // 26 case animations::TransitionSubType::CIRCLE: // 27 case animations::TransitionSubType::HEART: // 31 case animations::TransitionSubType::FANOUTHORIZONTAL: // 55 case animations::TransitionSubType::ACROSS: // 108 return true; default: return false; } } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::CROSSFADE ) { return true; } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::FADEOVERCOLOR ) { return true; } else if( transitionType == animations::TransitionType::IRISWIPE && transitionSubType == animations::TransitionSubType::DIAMOND ) { return true; } else if( transitionType == animations::TransitionType::ZOOM && transitionSubType == animations::TransitionSubType::ROTATEIN ) { return true; } else return false; } virtual uno::Reference< presentation::XTransition > SAL_CALL createTransition( sal_Int16 transitionType, sal_Int16 transitionSubType, sal_Int32 transitionFadeColor, const uno::Reference< presentation::XSlideShowView >& view, const uno::Reference< rendering::XBitmap >& leavingBitmap, const uno::Reference< rendering::XBitmap >& enteringBitmap ) override { if( !hasTransition( transitionType, transitionSubType ) ) return uno::Reference< presentation::XTransition >(); rtl::Reference< OGLTransitionerImpl > xRes( new OGLTransitionerImpl() ); if ( !xRes->initialize( view, leavingBitmap, enteringBitmap ) ) return uno::Reference< presentation::XTransition >(); std::shared_ptr pTransition; if( transitionType == animations::TransitionType::MISCSHAPEWIPE ) { switch( transitionSubType ) { case animations::TransitionSubType::LEFTTORIGHT: pTransition = makeFallLeaving(); break; case animations::TransitionSubType::TOPTOBOTTOM: pTransition = makeTurnAround(); break; case animations::TransitionSubType::TOPLEFT: pTransition = makeIris(); break; case animations::TransitionSubType::TOPRIGHT: pTransition = makeTurnDown(); break; case animations::TransitionSubType::BOTTOMRIGHT: pTransition = makeRochade(); break; case animations::TransitionSubType::BOTTOMLEFT: pTransition = makeVenetianBlinds( true, 8 ); break; case animations::TransitionSubType::TOPCENTER: pTransition = makeVenetianBlinds( false, 6 ); break; case animations::TransitionSubType::RIGHTCENTER: pTransition = makeStatic(); break; case animations::TransitionSubType::BOTTOMCENTER: pTransition = makeDissolve(); break; case animations::TransitionSubType::CORNERSIN: pTransition = makeInsideCubeFaceToLeft(); break; case animations::TransitionSubType::CORNERSOUT: pTransition = makeOutsideCubeFaceToLeft(); break; case animations::TransitionSubType::VERTICAL: pTransition = makeVortex(); break; case animations::TransitionSubType::HORIZONTAL: pTransition = makeRipple(); break; case animations::TransitionSubType::CIRCLE: pTransition = makeRevolvingCircles(8,128); break; case animations::TransitionSubType::FANOUTHORIZONTAL: pTransition = makeHelix(20); break; case animations::TransitionSubType::ACROSS: pTransition = makeNByMTileFlip(8,6); break; case animations::TransitionSubType::DIAMOND: pTransition = makeGlitter(); break; case animations::TransitionSubType::HEART: pTransition = makeHoneycomb(); break; } } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::CROSSFADE ) { pTransition = makeFadeSmoothly(); } else if( transitionType == animations::TransitionType::FADE && transitionSubType == animations::TransitionSubType::FADEOVERCOLOR ) { pTransition = makeFadeThroughColor( transitionFadeColor == 0xffffff ); } else if( transitionType == animations::TransitionType::IRISWIPE && transitionSubType == animations::TransitionSubType::DIAMOND ) { pTransition = makeDiamond(); } else if( transitionType == animations::TransitionType::ZOOM && transitionSubType == animations::TransitionSubType::ROTATEIN ) { pTransition = makeNewsflash(); } if ( !pTransition || !xRes->setTransition(pTransition) ) return uno::Reference< presentation::XTransition >(); return xRes; } }; } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* slideshow_OGLTransitionFactoryImpl_get_implementation( css::uno::XComponentContext* , css::uno::Sequence const&) { return cppu::acquire(new OGLTransitionFactoryImpl()); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */