diff options
author | Thorsten Behrens <tbehrens@suse.com> | 2012-01-31 01:07:48 +0100 |
---|---|---|
committer | Thorsten Behrens <tbehrens@suse.com> | 2013-10-07 17:33:45 +0200 |
commit | e52f1bd7b34fc73f52aadf1d33efa6685a0b22e8 (patch) | |
tree | 6457d6bf15886430264fa805d7e56352484e8813 /canvas | |
parent | 21ec9beae29b19b8ec6f0a16fd0e708e4f210208 (diff) |
Add opengl canvas implementation.
Adds opengl canvas implementation - display-list-based, all
rendering done as textured geometry. Needs shader support.
Currently compiles and works on Linux, Mac should be ~easy to
add, win32 eventually.
Change-Id: Ibf3eb88d6a36a91b2960a3a6320d708160e4fc14
Diffstat (limited to 'canvas')
25 files changed, 4652 insertions, 0 deletions
diff --git a/canvas/Library_oglcanvas.mk b/canvas/Library_oglcanvas.mk new file mode 100644 index 000000000000..fca6996f70d3 --- /dev/null +++ b/canvas/Library_oglcanvas.mk @@ -0,0 +1,69 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,oglcanvas)) + +$(eval $(call gb_Library_set_componentfile,oglcanvas,canvas/source/opengl/oglcanvas)) + +$(eval $(call gb_Library_use_sdk_api,oglcanvas)) + +$(eval $(call gb_Library_use_libraries,oglcanvas,\ + sal \ + cppu \ + basegfx \ + cppuhelper \ + comphelper \ + vcl \ + tk \ + tl \ + i18nlangtag \ + canvastools \ + $(gb_UWINAPI) \ +)) + +$(eval $(call gb_Library_add_exception_objects,oglcanvas,\ + canvas/source/opengl/ogl_bitmapcanvashelper \ + canvas/source/opengl/ogl_canvasbitmap \ + canvas/source/opengl/ogl_canvascustomsprite \ + canvas/source/opengl/ogl_canvasfont \ + canvas/source/opengl/ogl_canvashelper \ + canvas/source/opengl/ogl_canvastools \ + canvas/source/opengl/ogl_spritecanvas \ + canvas/source/opengl/ogl_spritedevicehelper \ + canvas/source/opengl/ogl_textlayout \ + canvas/source/opengl/ogl_texturecache \ +)) + +$(eval $(call gb_Library_use_externals,oglcanvas,\ + boost_headers \ +)) + +ifeq ($(strip $(OS)),MACOSX) +$(eval $(call gb_Library_use_system_darwin_frameworks,oglcanvas,\ + Cocoa \ + GLUT \ + OpenGL \ +)) + +else ifeq ($(strip $(OS)),WNT) +$(eval $(call gb_Library_use_system_win32_libs,oglcanvas,\ + gdi32 \ + glu32 \ + opengl32 \ +)) + +else +$(eval $(call gb_Library_add_libs,oglcanvas,\ + -lGL \ + -lGLU \ + -lX11 \ +)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/canvas/Module_canvas.mk b/canvas/Module_canvas.mk index 0bc364e532ce..510cb98b2004 100644 --- a/canvas/Module_canvas.mk +++ b/canvas/Module_canvas.mk @@ -32,6 +32,12 @@ $(eval $(call gb_Module_add_targets,canvas,\ )) endif +ifeq ($(ENABLE_OPENGL),TRUE) +$(eval $(call gb_Module_add_targets,canvas,\ + Library_oglcanvas \ +)) +endif + ifneq ($(ENABLE_DIRECTX),) $(eval $(call gb_Module_add_targets,canvas,\ Library_directx9canvas \ diff --git a/canvas/source/opengl/ogl_bitmapcanvashelper.cxx b/canvas/source/opengl/ogl_bitmapcanvashelper.cxx new file mode 100644 index 000000000000..1d132cbd257f --- /dev/null +++ b/canvas/source/opengl/ogl_bitmapcanvashelper.cxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_bitmapcanvashelper.hxx" + +#include <canvas/debug.hxx> +#include <canvas/canvastools.hxx> +#include <tools/diagnose_ex.h> + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + BitmapCanvasHelper::BitmapCanvasHelper() + {} + + void BitmapCanvasHelper::disposing() + { + CanvasHelper::disposing(); + } + + void BitmapCanvasHelper::init( rendering::XGraphicDevice& rDevice, + SpriteDeviceHelper& rDeviceHelper, + const geometry::IntegerSize2D& rSize ) + { + maSize = rSize; + CanvasHelper::init(rDevice,rDeviceHelper); + } + + void BitmapCanvasHelper::copyRect( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XBitmapCanvas >& /*sourceCanvas*/, + const geometry::RealRectangle2D& /*sourceRect*/, + const rendering::ViewState& /*sourceViewState*/, + const rendering::RenderState& /*sourceRenderState*/, + const geometry::RealRectangle2D& /*destRect*/, + const rendering::ViewState& /*destViewState*/, + const rendering::RenderState& /*destRenderState*/ ) + { + // TODO(F2): copyRect NYI + } + + geometry::IntegerSize2D BitmapCanvasHelper::getSize() + { + return maSize; + } + + uno::Reference< rendering::XBitmap > BitmapCanvasHelper::getScaledBitmap( const geometry::RealSize2D& /*newSize*/, + sal_Bool /*beFast*/ ) + { + // TODO(F1): + return uno::Reference< rendering::XBitmap >(); + } + + uno::Sequence< sal_Int8 > BitmapCanvasHelper::getData( rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const geometry::IntegerRectangle2D& /*rect*/ ) + { + // TODO(F2): NYI - and improbable to ever be + return uno::Sequence< sal_Int8 >(); + } + + void BitmapCanvasHelper::setData( const uno::Sequence< sal_Int8 >& /*data*/, + const rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const geometry::IntegerRectangle2D& /*rect*/ ) + { + // TODO(F2): NYI - and improbable to ever be + } + + void BitmapCanvasHelper::setPixel( const uno::Sequence< sal_Int8 >& /*color*/, + const rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const geometry::IntegerPoint2D& /*pos*/ ) + { + // TODO(F2): NYI - and improbable to ever be + } + + uno::Sequence< sal_Int8 > BitmapCanvasHelper::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const geometry::IntegerPoint2D& /*pos*/ ) + { + // TODO(F2): NYI - and improbable to ever be + return uno::Sequence< sal_Int8 >(); + } + + rendering::IntegerBitmapLayout BitmapCanvasHelper::getMemoryLayout() + { + return ::canvas::tools::getStdMemoryLayout(getSize()); + } + + bool BitmapCanvasHelper::hasAlpha() const + { + return true; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_bitmapcanvashelper.hxx b/canvas/source/opengl/ogl_bitmapcanvashelper.hxx new file mode 100644 index 000000000000..50781c3bb491 --- /dev/null +++ b/canvas/source/opengl/ogl_bitmapcanvashelper.hxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_BITMAPCANVASHELPER_HXX_ +#define OGL_BITMAPCANVASHELPER_HXX_ + +#include <com/sun/star/rendering/XBitmapCanvas.hpp> +#include <com/sun/star/rendering/XIntegerBitmap.hpp> + +#include <basegfx/vector/b2isize.hxx> +#include <basegfx/vector/b2dsize.hxx> + +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <vector> + +#include "ogl_canvashelper.hxx" + + +namespace oglcanvas +{ + /** Helper class for basic canvas functionality. */ + class BitmapCanvasHelper : public CanvasHelper + { + public: + BitmapCanvasHelper(); + + /// Release all references + void disposing(); + + /** Initialize canvas helper + + This method late-initializes the canvas helper, providing + it with the necessary device and output objects. Note that + the CanvasHelper does <em>not</em> take ownership of the + passed rDevice reference, nor does it perform any + reference counting. Thus, to prevent the reference counted + SpriteCanvas object from deletion, the user of this class + is responsible for holding ref-counted references itself! + + @param rDevice + Reference device this canvas is associated with + + */ + void init( ::com::sun::star::rendering::XGraphicDevice& rDevice, + SpriteDeviceHelper& rDeviceHelper, + const ::com::sun::star::geometry::IntegerSize2D& rSize ); + + // BitmapCanvasHelper functionality + // ================================ + + void copyRect( const ::com::sun::star::rendering::XCanvas* rCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XBitmapCanvas >& sourceCanvas, + const ::com::sun::star::geometry::RealRectangle2D& sourceRect, + const ::com::sun::star::rendering::ViewState& sourceViewState, + const ::com::sun::star::rendering::RenderState& sourceRenderState, + const ::com::sun::star::geometry::RealRectangle2D& destRect, + const ::com::sun::star::rendering::ViewState& destViewState, + const ::com::sun::star::rendering::RenderState& destRenderState ); + + ::com::sun::star::geometry::IntegerSize2D getSize(); + + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmapCanvas > queryBitmapCanvas(); + + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmap > + getScaledBitmap( const ::com::sun::star::geometry::RealSize2D& newSize, + sal_Bool beFast ); + + ::com::sun::star::uno::Sequence< sal_Int8 > + getData( ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerRectangle2D& rect ); + + void setData( const ::com::sun::star::uno::Sequence< sal_Int8 >& data, + const ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerRectangle2D& rect ); + + void setPixel( const ::com::sun::star::uno::Sequence< sal_Int8 >& color, + const ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerPoint2D& pos ); + + ::com::sun::star::uno::Sequence< sal_Int8 > + getPixel( ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerPoint2D& pos ); + + ::com::sun::star::rendering::IntegerBitmapLayout getMemoryLayout(); + + bool hasAlpha() const; + + private: + ::com::sun::star::geometry::IntegerSize2D maSize; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_buffercontext.hxx b/canvas/source/opengl/ogl_buffercontext.hxx new file mode 100644 index 000000000000..b9964126f025 --- /dev/null +++ b/canvas/source/opengl/ogl_buffercontext.hxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_BUFFERCONTEXT_HXX_ +#define OGL_BUFFERCONTEXT_HXX_ + +#include <sal/config.h> +#include <boost/shared_ptr.hpp> + +namespace oglcanvas +{ + struct IBufferContext + { + virtual ~IBufferContext() {} + + /// start render to buffer. changes gl current context + virtual bool startBufferRendering() = 0; + + /// end render to buffer. switches to window context, and selects rendered texture + virtual bool endBufferRendering() = 0; + }; + + typedef ::boost::shared_ptr<IBufferContext> IBufferContextSharedPtr; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvasbitmap.cxx b/canvas/source/opengl/ogl_canvasbitmap.cxx new file mode 100644 index 000000000000..d78baf58bd97 --- /dev/null +++ b/canvas/source/opengl/ogl_canvasbitmap.cxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_canvasbitmap.hxx" + +#include <canvas/debug.hxx> +#include <canvas/canvastools.hxx> +#include <tools/diagnose_ex.h> + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + CanvasBitmap::CanvasBitmap( const geometry::IntegerSize2D& rSize, + const SpriteCanvasRef& rDevice, + SpriteDeviceHelper& rDeviceHelper, + bool bHasAlpha ) : + mpDevice( rDevice ), + mbHasAlpha( bHasAlpha ) + { + ENSURE_OR_THROW( mpDevice.is(), + "CanvasBitmap::CanvasBitmap(): Invalid surface or device" ); + + maCanvasHelper.init( *mpDevice.get(), rDeviceHelper, rSize ); + } + + CanvasBitmap::CanvasBitmap( const CanvasBitmap& rSrc ) : + mpDevice( rSrc.mpDevice ), + mbHasAlpha( rSrc.mbHasAlpha ) + { + maCanvasHelper = rSrc.maCanvasHelper; + } + + void SAL_CALL CanvasBitmap::disposeThis() + { + mpDevice.clear(); + + // forward to parent + CanvasBitmapBaseT::disposeThis(); + } + + bool CanvasBitmap::renderRecordedActions() const + { + return maCanvasHelper.renderRecordedActions(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvasbitmap.hxx b/canvas/source/opengl/ogl_canvasbitmap.hxx new file mode 100644 index 000000000000..b874bde8da42 --- /dev/null +++ b/canvas/source/opengl/ogl_canvasbitmap.hxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_CANVASBITMAP_HXX +#define OGL_CANVASBITMAP_HXX + +#include <cppuhelper/compbase2.hxx> + +#include <com/sun/star/rendering/XBitmapCanvas.hpp> +#include <com/sun/star/rendering/XIntegerBitmap.hpp> + +#include <canvas/base/integerbitmapbase.hxx> +#include <canvas/base/disambiguationhelper.hxx> +#include <basegfx/vector/b2isize.hxx> + +#include <boost/shared_ptr.hpp> + +#include "ogl_bitmapcanvashelper.hxx" +#include "ogl_spritecanvas.hxx" + + +/* Definition of CanvasBitmap class */ + +namespace oglcanvas +{ + typedef ::cppu::WeakComponentImplHelper2< ::com::sun::star::rendering::XBitmapCanvas, + ::com::sun::star::rendering::XIntegerBitmap > CanvasBitmapBase_Base; + typedef ::canvas::IntegerBitmapBase< + ::canvas::DisambiguationHelper< CanvasBitmapBase_Base >, + BitmapCanvasHelper, + ::osl::MutexGuard, + ::cppu::OWeakObject > CanvasBitmapBaseT; + + class CanvasBitmap : public CanvasBitmapBaseT + { + public: + /** Create a canvas bitmap for the given surface + + @param rSize + Size of the bitmap + + @param rDevice + Reference device, with which bitmap should be compatible + */ + CanvasBitmap( const ::com::sun::star::geometry::IntegerSize2D& rSize, + const SpriteCanvasRef& rDevice, + SpriteDeviceHelper& rDeviceHelper, + bool bHasAlpha ); + + /** Create verbatim copy (including all recorded actions) + */ + CanvasBitmap( const CanvasBitmap& rSrc ); + + /// Dispose all internal references + virtual void disposeThis(); + + /** Write out recorded actions + */ + bool renderRecordedActions() const; + + private: + /** MUST hold here, too, since CanvasHelper only contains a + raw pointer (without refcounting) + */ + SpriteCanvasRef mpDevice; + bool mbHasAlpha; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvascustomsprite.cxx b/canvas/source/opengl/ogl_canvascustomsprite.cxx new file mode 100644 index 000000000000..7c0867176438 --- /dev/null +++ b/canvas/source/opengl/ogl_canvascustomsprite.cxx @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_canvascustomsprite.hxx" +#include "ogl_canvastools.hxx" +#include "ogl_tools.hxx" + +#include <canvas/debug.hxx> +#include <canvas/verbosetrace.hxx> +#include <canvas/verifyinput.hxx> +#include <tools/diagnose_ex.h> + +#include <canvas/canvastools.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> + +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glext.h> + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + CanvasCustomSprite::CanvasCustomSprite( const ::com::sun::star::geometry::RealSize2D& rSpriteSize, + const SpriteCanvasRef& rRefDevice, + SpriteDeviceHelper& rDeviceHelper ) : + mpSpriteCanvas( rRefDevice ), + maSize(rSpriteSize), + mxClip(), + maTransformation(), + maPosition(), + mfAlpha(0.0), + mfPriority(0.0) + { + ENSURE_OR_THROW( rRefDevice.get(), + "CanvasCustomSprite::CanvasCustomSprite(): Invalid sprite canvas" ); + + ::canvas::tools::setIdentityAffineMatrix2D(maTransformation); + maCanvasHelper.init( *rRefDevice.get(), + rDeviceHelper ); + } + + void SAL_CALL CanvasCustomSprite::disposeThis() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + mpSpriteCanvas.clear(); + + // forward to parent + CanvasCustomSpriteBaseT::disposeThis(); + } + + void SAL_CALL CanvasCustomSprite::setAlpha( double alpha ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + canvas::tools::verifyRange( alpha, 0.0, 1.0 ); + + ::osl::MutexGuard aGuard( m_aMutex ); + mfAlpha = alpha; + } + + void SAL_CALL CanvasCustomSprite::move( const geometry::RealPoint2D& aNewPos, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + canvas::tools::verifyArgs(aNewPos, viewState, renderState, + BOOST_CURRENT_FUNCTION, + static_cast< ::cppu::OWeakObject* >(this)); + + ::osl::MutexGuard aGuard( m_aMutex ); + ::basegfx::B2DHomMatrix aTransform; + ::canvas::tools::mergeViewAndRenderTransform(aTransform, + viewState, + renderState); + + // convert position to device pixel + maPosition = ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos); + maPosition *= aTransform; + } + + void SAL_CALL CanvasCustomSprite::transform( const geometry::AffineMatrix2D& aTransformation ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + maTransformation = aTransformation; + } + + void SAL_CALL CanvasCustomSprite::clip( const uno::Reference< rendering::XPolyPolygon2D >& xClip ) throw (uno::RuntimeException) + { + mxClip = xClip; + } + + void SAL_CALL CanvasCustomSprite::setPriority( double nPriority ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + mfPriority = nPriority; + } + + void SAL_CALL CanvasCustomSprite::show() throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if( mpSpriteCanvas.is() ) + mpSpriteCanvas->show(this); + } + + void SAL_CALL CanvasCustomSprite::hide() throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if( mpSpriteCanvas.is() ) + mpSpriteCanvas->hide(this); + } + + uno::Reference< rendering::XCanvas > SAL_CALL CanvasCustomSprite::getContentCanvas() throw (uno::RuntimeException) + { + return this; + } + + bool CanvasCustomSprite::renderSprite() const + { + if( ::basegfx::fTools::equalZero( mfAlpha ) ) + return true; + + TransformationPreserver aPreserver1; + const ::basegfx::B2IVector aSpriteSizePixel( + ::canvas::tools::roundUp( maSize.Width ), + ::canvas::tools::roundUp( maSize.Height )); + + // translate sprite to output position + glTranslated(maPosition.getX(), maPosition.getY(), 0); + + { + TransformationPreserver aPreserver2; + + // apply sprite content transformation matrix + double aGLTransform[] = + { + maTransformation.m00, maTransformation.m10, 0, 0, + maTransformation.m01, maTransformation.m11, 0, 0, + 0, 0, 1, 0, + maTransformation.m02, maTransformation.m12, 0, 1 + }; + glMultMatrixd(aGLTransform); + + IBufferContextSharedPtr pBufferContext; + if( mfAlpha != 1.0 || mxClip.is() ) + { + // drats. need to render to temp surface before, and then + // composite that to screen + + // TODO(P3): buffer pbuffer, maybe even keep content + // (in a texture?) + pBufferContext=maCanvasHelper.getDeviceHelper()->createBufferContext(aSpriteSizePixel); + pBufferContext->startBufferRendering(); + } + + // this ends up in pBufferContext, if that one's "current" + if( !maCanvasHelper.renderRecordedActions() ) + return false; + + if( pBufferContext ) + { + // content ended up in background buffer - compose to + // screen now. Calls below switches us back to window + // context, and binds to generated, dynamic texture + pBufferContext->endBufferRendering(); + + glEnable(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA); + + // blend against fixed vertex color; texture alpha is multiplied in + glColor4f(1,1,1,mfAlpha); + + if( mxClip.is() ) + { + const double fWidth=maSize.Width; + const double fHeight=maSize.Height; + + // TODO(P3): buffer triangulation + const ::basegfx::B2DPolygon& rTriangulatedPolygon( + ::basegfx::triangulator::triangulate( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(mxClip))); + + basegfx::B2DPolygon rTriangleList( + basegfx::tools::clipTriangleListOnRange( + rTriangulatedPolygon, + basegfx::B2DRange( + 0,0, + aSpriteSizePixel.getX(), + aSpriteSizePixel.getY()))); + + glBegin(GL_TRIANGLES); + for( sal_uInt32 i=0; i<rTriangulatedPolygon.count(); i++ ) + { + const ::basegfx::B2DPoint& rPt( rTriangulatedPolygon.getB2DPoint(i) ); + const double s(rPt.getX()/fWidth); + const double t(rPt.getY()/fHeight); + glTexCoord2f(s,t); glVertex2d(rPt.getX(), rPt.getY()); + } + glEnd(); + } + else + { + const double fWidth=maSize.Width/aSpriteSizePixel.getX(); + const double fHeight=maSize.Height/aSpriteSizePixel.getY(); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0,0); glVertex2d(0,0); + glTexCoord2f(0,fHeight); glVertex2d(0, aSpriteSizePixel.getY()); + glTexCoord2f(fWidth,0); glVertex2d(aSpriteSizePixel.getX(),0); + glTexCoord2f(fWidth,fHeight); glVertex2d(aSpriteSizePixel.getX(),aSpriteSizePixel.getY()); + glEnd(); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + } + } + + glColor4f(1,0,0,1); + glBegin(GL_LINE_STRIP); + glVertex2d(-2,-2); + glVertex2d(-2,maSize.Height+4); + glVertex2d(maSize.Width+4,maSize.Height+4); + glVertex2d(maSize.Width+4,-2); + glVertex2d(-2,-2); + glVertex2d(maSize.Width+4,maSize.Height+4); + glEnd(); + + std::vector<double> aVec; + aVec.push_back(mfAlpha); + aVec.push_back(mfPriority); + aVec.push_back(maCanvasHelper.getRecordedActionCount()); + renderOSD( aVec, 10 ); + + return true; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvascustomsprite.hxx b/canvas/source/opengl/ogl_canvascustomsprite.hxx new file mode 100644 index 000000000000..a96ce6494d08 --- /dev/null +++ b/canvas/source/opengl/ogl_canvascustomsprite.hxx @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_CANVASCUSTOMSPRITE_HXX +#define OGL_CANVASCUSTOMSPRITE_HXX + +#include <cppuhelper/compbase2.hxx> +#include <comphelper/uno3.hxx> + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/rendering/XCustomSprite.hpp> +#include <com/sun/star/rendering/XPolyPolygon2D.hpp> + +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/vector/b2isize.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> + +#include <canvas/base/disambiguationhelper.hxx> + +#include "ogl_spritecanvas.hxx" +#include "ogl_canvashelper.hxx" + + +namespace oglcanvas +{ + typedef ::cppu::WeakComponentImplHelper2< ::com::sun::star::rendering::XCustomSprite, + ::com::sun::star::rendering::XCanvas > CanvasCustomSpriteBase_Base; + typedef ::canvas::CanvasBase< + ::canvas::DisambiguationHelper< CanvasCustomSpriteBase_Base >, + CanvasHelper, + ::osl::MutexGuard, + ::cppu::OWeakObject > CanvasCustomSpriteBaseT; + + /* Definition of CanvasCustomSprite class */ + + class CanvasCustomSprite : public CanvasCustomSpriteBaseT + { + public: + /** Create a custom sprite + + @param rSpriteSize + Size of the sprite in pixel + + @param rRefDevice + Associated output device + + @param rSpriteCanvas + Target canvas + + @param rDevice + Target DX device + */ + CanvasCustomSprite( const ::com::sun::star::geometry::RealSize2D& rSpriteSize, + const SpriteCanvasRef& rRefDevice, + SpriteDeviceHelper& rDeviceHelper ); + + virtual void disposeThis(); + + // XSprite + virtual void SAL_CALL setAlpha( double alpha ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL move( const ::com::sun::star::geometry::RealPoint2D& aNewPos, const ::com::sun::star::rendering::ViewState& viewState, const ::com::sun::star::rendering::RenderState& renderState ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL transform( const ::com::sun::star::geometry::AffineMatrix2D& aTransformation ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL clip( const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D >& aClip ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setPriority( double nPriority ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL show() throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL hide() throw (::com::sun::star::uno::RuntimeException); + + // XCustomSprite + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCanvas > SAL_CALL getContentCanvas() throw (::com::sun::star::uno::RuntimeException); + + double getPriority() const { return mfPriority; } + + /// Render sprite content at sprite position + bool renderSprite() const; + + private: + /** MUST hold here, too, since CanvasHelper only contains a + raw pointer (without refcounting) + */ + SpriteCanvasRef mpSpriteCanvas; + const ::com::sun::star::geometry::RealSize2D maSize; + + ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D > mxClip; + ::com::sun::star::geometry::AffineMatrix2D maTransformation; + ::basegfx::B2DPoint maPosition; + double mfAlpha; + double mfPriority; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvasfont.cxx b/canvas/source/opengl/ogl_canvasfont.cxx new file mode 100644 index 000000000000..d465919ab2b3 --- /dev/null +++ b/canvas/source/opengl/ogl_canvasfont.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_canvasfont.hxx" +#include "ogl_textlayout.hxx" + +#include <com/sun/star/rendering/XSpriteCanvas.hpp> +#include <com/sun/star/rendering/PanoseWeight.hpp> + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + CanvasFont::CanvasFont( const rendering::FontRequest& rFontRequest, + const uno::Sequence< beans::PropertyValue >& /*extraFontProperties*/, + const geometry::Matrix2D& fontMatrix ) : + CanvasFontBaseT( m_aMutex ), + maFontRequest( rFontRequest ), + maFontMatrix( fontMatrix ) + { + } + + void SAL_CALL CanvasFont::disposing() + { + ::osl::MutexGuard aGuard( m_aMutex ); + } + + uno::Reference< rendering::XTextLayout > SAL_CALL CanvasFont::createTextLayout( const rendering::StringContext& aText, + sal_Int8 nDirection, + sal_Int64 nRandomSeed ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return new TextLayout( aText, nDirection, nRandomSeed, ImplRef( this ) ); + } + + uno::Sequence< double > SAL_CALL CanvasFont::getAvailableSizes( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return uno::Sequence< double >(); + } + + uno::Sequence< beans::PropertyValue > SAL_CALL CanvasFont::getExtraFontProperties( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return uno::Sequence< beans::PropertyValue >(); + } + + rendering::FontRequest SAL_CALL CanvasFont::getFontRequest( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return maFontRequest; + } + + rendering::FontMetrics SAL_CALL CanvasFont::getFontMetrics( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return rendering::FontMetrics(); + } + + const ::com::sun::star::geometry::Matrix2D& CanvasFont::getFontMatrix() const + { + return maFontMatrix; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvasfont.hxx b/canvas/source/opengl/ogl_canvasfont.hxx new file mode 100644 index 000000000000..9a262975cf11 --- /dev/null +++ b/canvas/source/opengl/ogl_canvasfont.hxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_CANVASFONT_HXX +#define OGL_CANVASFONT_HXX + +#include <comphelper/implementationreference.hxx> + +#include <cppuhelper/compbase1.hxx> +#include <comphelper/broadcasthelper.hxx> + +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/rendering/XCanvasFont.hpp> + +#include <rtl/ref.hxx> + +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + + +/* Definition of CanvasFont class */ + +namespace oglcanvas +{ + class SpriteCanvas; + + typedef ::cppu::WeakComponentImplHelper1< ::com::sun::star::rendering::XCanvasFont > CanvasFontBaseT; + + class CanvasFont : public ::comphelper::OBaseMutex, + public CanvasFontBaseT, + private ::boost::noncopyable + { + public: + typedef ::comphelper::ImplementationReference< + CanvasFont, + ::com::sun::star::rendering::XCanvasFont > ImplRef; + + CanvasFont( const ::com::sun::star::rendering::FontRequest& fontRequest, + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& extraFontProperties, + const ::com::sun::star::geometry::Matrix2D& fontMatrix ); + + /// Dispose all internal references + virtual void SAL_CALL disposing(); + + // XCanvasFont + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XTextLayout > SAL_CALL createTextLayout( const ::com::sun::star::rendering::StringContext& aText, sal_Int8 nDirection, sal_Int64 nRandomSeed ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::rendering::FontRequest SAL_CALL getFontRequest( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::rendering::FontMetrics SAL_CALL getFontMetrics( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< double > SAL_CALL getAvailableSizes( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > SAL_CALL getExtraFontProperties( ) throw (::com::sun::star::uno::RuntimeException); + + const ::com::sun::star::geometry::Matrix2D& getFontMatrix() const; + + private: + ::com::sun::star::rendering::FontRequest maFontRequest; + ::com::sun::star::geometry::Matrix2D maFontMatrix; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvashelper.cxx b/canvas/source/opengl/ogl_canvashelper.cxx new file mode 100644 index 000000000000..ae349b999839 --- /dev/null +++ b/canvas/source/opengl/ogl_canvashelper.cxx @@ -0,0 +1,1011 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#define GL_GLEXT_PROTOTYPES + +#include "ogl_canvashelper.hxx" + +#include <rtl/crc.h> +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> + +#include <com/sun/star/rendering/TexturingMode.hpp> +#include <com/sun/star/rendering/CompositeOperation.hpp> +#include <com/sun/star/rendering/RepaintResult.hpp> +#include <com/sun/star/rendering/PathCapType.hpp> +#include <com/sun/star/rendering/PathJoinType.hpp> + +#include <vcl/virdev.hxx> +#include <vcl/metric.hxx> +#include <vcl/font.hxx> + +#include "ogl_canvasfont.hxx" +#include "ogl_canvastools.hxx" +#include "ogl_canvasbitmap.hxx" +#include "ogl_spritecanvas.hxx" +#include "ogl_texturecache.hxx" +#include "ogl_tools.hxx" + +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glext.h> + +#include <boost/scoped_array.hpp> + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + /* Concepts: + ========= + + This OpenGL canvas implementation tries to keep all render + output as high-level as possible, i.e. geometry data and + externally-provided bitmaps. Therefore, calls at the + XCanvas-interfaces are not immediately transformed into colored + pixel inside some GL buffer, but are retained simply with their + call parameters. Only after XSpriteCanvas::updateScreen() has + been called, this all gets transferred to the OpenGL subsystem + and converted to a visible scene. The big advantage is, this + makes sprite modifications practically zero-overhead, and saves + a lot on texture memory (compared to the directx canvas, which + immediately dumps every render call into a texture). + + The drawback, of course, is that complex images churn a lot of + GPU cycles on every re-rendering. + + For the while, I'll be using immediate mode, i.e. transfer data + over and over again to the OpenGL subsystem. Alternatively, + there are display lists, which at least keep the data on the + server, or even better, vertex buffers, which copy geometry + data over en bloc. + + Next todo: put polygon geometry into vertex buffer (LRU cache + necessary?) - or, rather, buffer objects! prune entries older + than one updateScreen() call) + + Text: http://www.opengl.org/resources/features/fontsurvey/ + */ + + struct CanvasHelper::Action + { + ::basegfx::B2DHomMatrix maTransform; + GLenum meSrcBlendMode; + GLenum meDstBlendMode; + rendering::ARGBColor maARGBColor; + ::basegfx::B2DPolyPolygonVector maPolyPolys; + + ::boost::function6< bool, + const CanvasHelper&, + const ::basegfx::B2DHomMatrix&, + GLenum, + GLenum, + const rendering::ARGBColor&, + const ::basegfx::B2DPolyPolygonVector& > maFunction; + }; + + namespace + { + bool lcl_drawPoint( const CanvasHelper& /*rHelper*/, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::ARGBColor& rColor, + const geometry::RealPoint2D& rPoint ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rColor); + + glBegin(GL_POINTS); + glVertex2d(rPoint.X, rPoint.Y); + glEnd(); + + return true; + } + + bool lcl_drawLine( const CanvasHelper& /*rHelper*/, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::ARGBColor& rColor, + const geometry::RealPoint2D& rStartPoint, + const geometry::RealPoint2D& rEndPoint ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rColor); + + glBegin(GL_LINES); + glVertex2d(rStartPoint.X, rStartPoint.Y); + glVertex2d(rEndPoint.X, rEndPoint.Y); + glEnd(); + + return true; + } + + bool lcl_drawPolyPolygon( const CanvasHelper& /*rHelper*/, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::ARGBColor& rColor, + const ::basegfx::B2DPolyPolygonVector& rPolyPolygons ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rColor); + + ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin(); + const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end(); + while( aCurr != aEnd ) + renderPolyPolygon(*aCurr++); + + return true; + } + + bool lcl_fillPolyPolygon( const CanvasHelper& /*rHelper*/, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::ARGBColor& rColor, + const ::basegfx::B2DPolyPolygonVector& rPolyPolygons ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rColor); + + ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin(); + const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end(); + while( aCurr != aEnd ) + { + glBegin(GL_TRIANGLES); + renderComplexPolyPolygon(*aCurr++); + glEnd(); + } + + return true; + } + + bool lcl_fillGradientPolyPolygon( const CanvasHelper& rHelper, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const ::canvas::ParametricPolyPolygon::Values& rValues, + const rendering::Texture& rTexture, + const ::basegfx::B2DPolyPolygonVector& rPolyPolygons ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor()); + + // convert to weird canvas textur coordinate system (not + // [0,1]^2, but path coordinate system) + ::basegfx::B2DHomMatrix aTextureTransform; + ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform, + rTexture.AffineTransform ); + ::basegfx::B2DRange aBounds; + ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin(); + const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end(); + while( aCurr != aEnd ) + aBounds.expand(::basegfx::tools::getRange(*aCurr++)); + aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY()); + aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight()); + + const sal_Int32 nNumCols=rValues.maColors.getLength(); + uno::Sequence< rendering::ARGBColor > aColors(nNumCols); + rendering::ARGBColor* const pColors=aColors.getArray(); + rendering::ARGBColor* pCurrCol=pColors; + for( sal_Int32 i=0; i<nNumCols; ++i ) + *pCurrCol++ = rHelper.getDevice()->getDeviceColorSpace()->convertToARGB(rValues.maColors[i])[0]; + + OSL_ASSERT(nNumCols == rValues.maStops.getLength()); + + switch( rValues.meType ) + { + case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR: + rHelper.getDeviceHelper()->useLinearGradientShader(pColors, + rValues.maStops, + aTextureTransform); + break; + + case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL: + rHelper.getDeviceHelper()->useRadialGradientShader(pColors, + rValues.maStops, + aTextureTransform); + break; + + case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR: + rHelper.getDeviceHelper()->useRectangularGradientShader(pColors, + rValues.maStops, + aTextureTransform); + break; + + default: + ENSURE_OR_THROW( false, + "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" ); + } + + + aCurr=rPolyPolygons.begin(); + while( aCurr != aEnd ) + { + glBegin(GL_TRIANGLES); + renderComplexPolyPolygon(*aCurr++); + glEnd(); + } + + glUseProgram(0); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + return true; + } + + bool lcl_drawOwnBitmap( const CanvasHelper& /*rHelper*/, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::ARGBColor& rColor, + const CanvasBitmap& rBitmap ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rColor); + + return rBitmap.renderRecordedActions(); + } + + bool lcl_drawGenericBitmap( const CanvasHelper& rHelper, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::ARGBColor& rColor, + const geometry::IntegerSize2D& rPixelSize, + const uno::Sequence<sal_Int8>& rPixelData, + sal_uInt32 nPixelCrc32 ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rColor); + + const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture( + rPixelSize, rPixelData.getConstArray(), nPixelCrc32); + + glBindTexture(GL_TEXTURE_2D, nTexId); + glEnable(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA); + + // blend against fixed vertex color; texture alpha is multiplied in + glColor4f(1,1,1,1); + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0,0); glVertex2d(0,0); + glTexCoord2f(0,1); glVertex2d(0, rPixelSize.Height); + glTexCoord2f(1,0); glVertex2d(rPixelSize.Width,0); + glTexCoord2f(1,1); glVertex2d(rPixelSize.Width,rPixelSize.Height); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + return true; + } + + bool lcl_fillTexturedPolyPolygon( const CanvasHelper& rHelper, + const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::Texture& rTexture, + const geometry::IntegerSize2D& rPixelSize, + const uno::Sequence<sal_Int8>& rPixelData, + sal_uInt32 nPixelCrc32, + const ::basegfx::B2DPolyPolygonVector& rPolyPolygons ) + { + TransformationPreserver aPreserver; + setupState(rTransform, eSrcBlend, eDstBlend, rendering::ARGBColor()); + + const unsigned int nTexId=rHelper.getDeviceHelper()->getTextureCache().getTexture( + rPixelSize, rPixelData.getConstArray(), nPixelCrc32); + + glBindTexture(GL_TEXTURE_2D, nTexId); + glEnable(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA); + + // convert to weird canvas textur coordinate system (not + // [0,1]^2, but path coordinate system) + ::basegfx::B2DHomMatrix aTextureTransform; + ::basegfx::unotools::homMatrixFromAffineMatrix( aTextureTransform, + rTexture.AffineTransform ); + ::basegfx::B2DRange aBounds; + ::basegfx::B2DPolyPolygonVector::const_iterator aCurr=rPolyPolygons.begin(); + const ::basegfx::B2DPolyPolygonVector::const_iterator aEnd=rPolyPolygons.end(); + while( aCurr != aEnd ) + aBounds.expand(::basegfx::tools::getRange(*aCurr++)); + aTextureTransform.translate(-aBounds.getMinX(), -aBounds.getMinY()); + aTextureTransform.scale(1/aBounds.getWidth(), 1/aBounds.getHeight()); + aTextureTransform.invert(); + + glMatrixMode(GL_TEXTURE); + double aTexTransform[] = + { + aTextureTransform.get(0,0), aTextureTransform.get(1,0), 0, 0, + aTextureTransform.get(0,1), aTextureTransform.get(1,1), 0, 0, + 0, 0, 1, 0, + aTextureTransform.get(0,2), aTextureTransform.get(1,2), 0, 1 + }; + glLoadMatrixd(aTexTransform); + + // blend against fixed vertex color; texture alpha is multiplied in + glColor4f(1,1,1,rTexture.Alpha); + + aCurr=rPolyPolygons.begin(); + while( aCurr != aEnd ) + { + glBegin(GL_TRIANGLES); + renderComplexPolyPolygon(*aCurr++); + glEnd(); + } + + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + return true; + } + } + + CanvasHelper::CanvasHelper() : + mpDevice( NULL ), + mpRecordedActions() + {} + + CanvasHelper::~CanvasHelper() + {} + + CanvasHelper& CanvasHelper::operator=( const CanvasHelper& rSrc ) + { + mpDevice = rSrc.mpDevice; + mpDeviceHelper = rSrc.mpDeviceHelper; + mpRecordedActions = rSrc.mpRecordedActions; + return *this; + } + + void CanvasHelper::disposing() + { + RecordVectorT aThrowaway; + mpRecordedActions.swap( aThrowaway ); + mpDevice = NULL; + mpDeviceHelper = NULL; + } + + void CanvasHelper::init( rendering::XGraphicDevice& rDevice, + SpriteDeviceHelper& rDeviceHelper ) + { + mpDevice = &rDevice; + mpDeviceHelper = &rDeviceHelper; + } + + void CanvasHelper::clear() + { + mpRecordedActions->clear(); + } + + void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/, + const geometry::RealPoint2D& aPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + if( mpDevice ) + { + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maFunction = ::boost::bind(&lcl_drawPoint, + _1,_2,_3,_4,_5, + aPoint); + } + } + + void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/, + const geometry::RealPoint2D& aStartPoint, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + if( mpDevice ) + { + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maFunction = ::boost::bind(&lcl_drawLine, + _1,_2,_3,_4,_5, + aStartPoint,aEndPoint); + } + } + + void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/, + const geometry::RealBezierSegment2D& aBezierSegment, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + if( mpDevice ) + { + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + + // TODO(F2): subdivide&render whole curve + rAct.maFunction = ::boost::bind(&lcl_drawLine, + _1,_2,_3,_4,_5, + geometry::RealPoint2D( + aBezierSegment.Px, + aBezierSegment.Py), + aEndPoint); + } + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ENSURE_OR_THROW( xPolyPolygon.is(), + "CanvasHelper::drawPolyPolygon: polygon is NULL"); + + if( mpDevice ) + { + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maPolyPolys.push_back( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon)); + rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety + + rAct.maFunction = &lcl_drawPolyPolygon; + } + + // TODO(P1): Provide caching here. + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const rendering::StrokeAttributes& /*strokeAttributes*/ ) + { + ENSURE_OR_THROW( xPolyPolygon.is(), + "CanvasHelper::strokePolyPolygon: polygon is NULL"); + + if( mpDevice ) + { + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maPolyPolys.push_back( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon)); + rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety + + // TODO(F3): fallback to drawPolyPolygon currently + rAct.maFunction = &lcl_drawPolyPolygon; + } + + // TODO(P1): Provide caching here. + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + const uno::Sequence< rendering::Texture >& /*textures*/, + const rendering::StrokeAttributes& /*strokeAttributes*/ ) + { + // TODO + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + const uno::Sequence< rendering::Texture >& /*textures*/, + const uno::Reference< geometry::XMapping2D >& /*xMapping*/, + const rendering::StrokeAttributes& /*strokeAttributes*/ ) + { + // TODO + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + const rendering::StrokeAttributes& /*strokeAttributes*/ ) + { + // TODO + return uno::Reference< rendering::XPolyPolygon2D >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ENSURE_OR_THROW( xPolyPolygon.is(), + "CanvasHelper::fillPolyPolygon: polygon is NULL"); + + if( mpDevice ) + { + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maPolyPolys.push_back( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon)); + rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety + + rAct.maFunction = &lcl_fillPolyPolygon; + } + + // TODO(P1): Provide caching here. + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures ) + { + ENSURE_OR_THROW( xPolyPolygon.is(), + "CanvasHelper::fillPolyPolygon: polygon is NULL"); + + if( mpDevice ) + { + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maPolyPolys.push_back( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon)); + rAct.maPolyPolys.back().makeUnique(); // own copy, for thread safety + + // TODO(F1): Multi-texturing + if( textures[0].Gradient.is() ) + { + // try to cast XParametricPolyPolygon2D reference to + // our implementation class. + ::canvas::ParametricPolyPolygon* pGradient = + dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() ); + + if( pGradient ) + { + // copy state from Gradient polypoly locally + // (given object might change!) + const ::canvas::ParametricPolyPolygon::Values& rValues( + pGradient->getValues() ); + + rAct.maFunction = ::boost::bind(&lcl_fillGradientPolyPolygon, + _1,_2,_3,_4, + rValues, + textures[0], + _6); + } + else + { + // TODO(F1): The generic case is missing here + ENSURE_OR_THROW( false, + "CanvasHelper::fillTexturedPolyPolygon(): unknown parametric polygon encountered" ); + } + } + else if( textures[0].Bitmap.is() ) + { + // own bitmap? + CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(textures[0].Bitmap.get()); + if( pOwnBitmap ) + { + // TODO(F2): own texture bitmap + } + else + { + // TODO(P3): Highly inefficient - simply copies pixel data + + uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap( + textures[0].Bitmap, + uno::UNO_QUERY); + if( xIntegerBitmap.is() ) + { + const geometry::IntegerSize2D aSize=xIntegerBitmap->getSize(); + rendering::IntegerBitmapLayout aLayout; + uno::Sequence<sal_Int8> aPixelData= + xIntegerBitmap->getData( + aLayout, + geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height)); + + // force-convert color to ARGB8888 int color space + uno::Sequence<sal_Int8> aARGBBytes( + aLayout.ColorSpace->convertToIntegerColorSpace( + aPixelData, + canvas::tools::getStdColorSpace())); + + rAct.maFunction = ::boost::bind(&lcl_fillTexturedPolyPolygon, + _1,_2,_3,_4, + textures[0], + aSize, + aARGBBytes, + rtl_crc32(0, + aARGBBytes.getConstArray(), + aARGBBytes.getLength()), + _6); + } + // TODO(F1): handle non-integer case + } + } + } + + // TODO(P1): Provide caching here. + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + const uno::Sequence< rendering::Texture >& /*textures*/, + const uno::Reference< geometry::XMapping2D >& /*xMapping*/ ) + { + // TODO + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* /*pCanvas*/, + const rendering::FontRequest& fontRequest, + const uno::Sequence< beans::PropertyValue >& extraFontProperties, + const geometry::Matrix2D& fontMatrix ) + { + if( mpDevice ) + return uno::Reference< rendering::XCanvasFont >( + new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) ); + + return uno::Reference< rendering::XCanvasFont >(); + } + + uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/, + const rendering::FontInfo& /*aFilter*/, + const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ ) + { + // TODO + return uno::Sequence< rendering::FontInfo >(); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* /*pCanvas*/, + const rendering::StringContext& /*text*/, + const uno::Reference< rendering::XCanvasFont >& /*xFont*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + sal_Int8 /*textDirection*/ ) + { + // TODO - but not used from slideshow + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XTextLayout >& xLayoutetText, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ENSURE_OR_THROW( xLayoutetText.is(), + "CanvasHelper::drawTextLayout: text is NULL"); + + if( mpDevice ) + { + VirtualDevice aVDev; + aVDev.EnableOutput(false); + + CanvasFont* pFont=dynamic_cast<CanvasFont*>(xLayoutetText->getFont().get()); + const rendering::StringContext& rTxt=xLayoutetText->getText(); + if( pFont && rTxt.Length ) + { + // create the font + const rendering::FontRequest& rFontRequest = pFont->getFontRequest(); + const geometry::Matrix2D& rFontMatrix = pFont->getFontMatrix(); + ::Font aFont( + rFontRequest.FontDescription.FamilyName, + rFontRequest.FontDescription.StyleName, + Size( 0, ::basegfx::fround(rFontRequest.CellSize))); + + aFont.SetAlign( ALIGN_BASELINE ); + aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); + aFont.SetVertical( (rFontRequest.FontDescription.IsVertical==util::TriState_YES) ? true : false ); + aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) ); + aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL ); + + // adjust to stretched font + if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11)) + { + const Size aSize = aVDev.GetFontMetric( aFont ).GetSize(); + const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 ); + double fStretch = (rFontMatrix.m00 + rFontMatrix.m01); + + if( !::basegfx::fTools::equalZero( fDividend) ) + fStretch /= fDividend; + + const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch ); + + aFont.SetWidth( nNewWidth ); + } + + // set font + aVDev.SetFont(aFont); + + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + + // handle custom spacing, if there + uno::Sequence<double> aLogicalAdvancements=xLayoutetText->queryLogicalAdvancements(); + if( aLogicalAdvancements.getLength() ) + { + // create the DXArray + const sal_Int32 nLen( aLogicalAdvancements.getLength() ); + ::boost::scoped_array<sal_Int32> pDXArray( new sal_Int32[nLen] ); + for( sal_Int32 i=0; i<nLen; ++i ) + pDXArray[i] = basegfx::fround( aLogicalAdvancements[i] ); + + // get the glyphs + aVDev.GetTextOutlines(rAct.maPolyPolys, + rTxt.Text, + 0, + (xub_StrLen)rTxt.StartPosition, + (xub_StrLen)rTxt.Length, + true, + 0, + pDXArray.get() ); + } + else + { + // get the glyphs + aVDev.GetTextOutlines(rAct.maPolyPolys, + rTxt.Text, + 0, + (xub_StrLen)rTxt.StartPosition, + (xub_StrLen)rTxt.Length ); + } + + // own copy, for thread safety + std::for_each(rAct.maPolyPolys.begin(), + rAct.maPolyPolys.end(), + ::boost::mem_fn(&::basegfx::B2DPolyPolygon::makeUnique)); + + rAct.maFunction = &lcl_fillPolyPolygon; + } + } + + // TODO + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/, + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ENSURE_OR_THROW( xBitmap.is(), + "CanvasHelper::drawBitmap: bitmap is NULL"); + + if( mpDevice ) + { + // own bitmap? + CanvasBitmap* pOwnBitmap=dynamic_cast<CanvasBitmap*>(xBitmap.get()); + if( pOwnBitmap ) + { + // insert as transformed copy of bitmap action vector - + // during rendering, this gets rendered into a temporary + // buffer, and then composited to the front + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maFunction = ::boost::bind(&lcl_drawOwnBitmap, + _1,_2,_3,_4,_5, + *pOwnBitmap); + } + else + { + // TODO(P3): Highly inefficient - simply copies pixel data + + uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntegerBitmap( + xBitmap, uno::UNO_QUERY); + if( xIntegerBitmap.is() ) + { + const geometry::IntegerSize2D aSize=xBitmap->getSize(); + rendering::IntegerBitmapLayout aLayout; + uno::Sequence<sal_Int8> aPixelData= + xIntegerBitmap->getData( + aLayout, + geometry::IntegerRectangle2D(0,0,aSize.Width,aSize.Height)); + + // force-convert color to ARGB8888 int color space + uno::Sequence<sal_Int8> aARGBBytes( + aLayout.ColorSpace->convertToIntegerColorSpace( + aPixelData, + canvas::tools::getStdColorSpace())); + + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + rAct.maFunction = ::boost::bind(&lcl_drawGenericBitmap, + _1,_2,_3,_4,_5, + aSize, aARGBBytes, + rtl_crc32(0, + aARGBBytes.getConstArray(), + aARGBBytes.getLength())); + } + // TODO(F1): handle non-integer case + } + } + + // TODO(P1): Provide caching here. + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas, + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + // TODO(F3): remove this wart altogether + return drawBitmap(pCanvas, xBitmap, viewState, renderState); + } + + uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice() + { + return uno::Reference< rendering::XGraphicDevice >(mpDevice); + } + + void CanvasHelper::setupGraphicsState( Action& o_action, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ENSURE_OR_THROW( mpDevice, + "CanvasHelper::setupGraphicsState: reference device invalid" ); + + // TODO(F3): clipping + // TODO(P2): think about caching transformations between canvas calls + + // setup overall transform only now. View clip above was + // relative to view transform + ::basegfx::B2DHomMatrix aTransform; + ::canvas::tools::mergeViewAndRenderTransform(o_action.maTransform, + viewState, + renderState); + // setup compositing - mapping courtesy David Reveman + // (glitz_operator.c) + switch( renderState.CompositeOperation ) + { + case rendering::CompositeOperation::OVER: + o_action.meSrcBlendMode=GL_ONE; + o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; + break; + case rendering::CompositeOperation::CLEAR: + o_action.meSrcBlendMode=GL_ZERO; + o_action.meDstBlendMode=GL_ZERO; + break; + case rendering::CompositeOperation::SOURCE: + o_action.meSrcBlendMode=GL_ONE; + o_action.meDstBlendMode=GL_ZERO; + break; + case rendering::CompositeOperation::UNDER: + // FALLTHROUGH intended - but correct?! + case rendering::CompositeOperation::DESTINATION: + o_action.meSrcBlendMode=GL_ZERO; + o_action.meDstBlendMode=GL_ONE; + break; + case rendering::CompositeOperation::INSIDE: + o_action.meSrcBlendMode=GL_DST_ALPHA; + o_action.meDstBlendMode=GL_ZERO; + break; + case rendering::CompositeOperation::INSIDE_REVERSE: + o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; + o_action.meDstBlendMode=GL_ZERO; + break; + case rendering::CompositeOperation::OUTSIDE: + o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; + o_action.meDstBlendMode=GL_ONE; + break; + case rendering::CompositeOperation::OUTSIDE_REVERSE: + o_action.meSrcBlendMode=GL_ZERO; + o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; + break; + case rendering::CompositeOperation::ATOP: + o_action.meSrcBlendMode=GL_DST_ALPHA; + o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; + break; + case rendering::CompositeOperation::ATOP_REVERSE: + o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; + o_action.meDstBlendMode=GL_SRC_ALPHA; + break; + case rendering::CompositeOperation::XOR: + o_action.meSrcBlendMode=GL_ONE_MINUS_DST_ALPHA; + o_action.meDstBlendMode=GL_ONE_MINUS_SRC_ALPHA; + break; + case rendering::CompositeOperation::ADD: + o_action.meSrcBlendMode=GL_ONE; + o_action.meDstBlendMode=GL_ONE; + break; + case rendering::CompositeOperation::SATURATE: + o_action.meSrcBlendMode=GL_SRC_ALPHA_SATURATE; + o_action.meDstBlendMode=GL_SRC_ALPHA_SATURATE; + break; + + default: + ENSURE_OR_THROW( false, "CanvasHelper::setupGraphicsState: unexpected mode" ); + break; + } + + o_action.maARGBColor = + mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]; + } + + void CanvasHelper::flush() const + { + } + + bool CanvasHelper::renderRecordedActions() const + { + std::vector<Action>::const_iterator aCurr(mpRecordedActions->begin()); + const std::vector<Action>::const_iterator aEnd(mpRecordedActions->end()); + while( aCurr != aEnd ) + { + if( !aCurr->maFunction( *this, + aCurr->maTransform, + aCurr->meSrcBlendMode, + aCurr->meDstBlendMode, + aCurr->maARGBColor, + aCurr->maPolyPolys ) ) + return false; + + ++aCurr; + } + + return true; + } + + size_t CanvasHelper::getRecordedActionCount() const + { + return mpRecordedActions->size(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvashelper.hxx b/canvas/source/opengl/ogl_canvashelper.hxx new file mode 100644 index 000000000000..b7280ac1c9a1 --- /dev/null +++ b/canvas/source/opengl/ogl_canvashelper.hxx @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_CANVASHELPER_HXX_ +#define OGL_CANVASHELPER_HXX_ + +#include <com/sun/star/rendering/XCanvas.hpp> + +#include <basegfx/vector/b2isize.hxx> +#include <basegfx/vector/b2dsize.hxx> + +#include <o3tl/cow_wrapper.hxx> +#include <vector> + +namespace oglcanvas +{ + class SpriteDeviceHelper; + + /** Helper class for basic canvas functionality. */ + class CanvasHelper + { + public: + CanvasHelper(); + + // outline because of incomplete type Action + ~CanvasHelper(); + CanvasHelper& operator=( const CanvasHelper& ); + + /// Release all references + void disposing(); + + /** Initialize canvas helper + + This method late-initializes the canvas helper, providing + it with the necessary device and output objects. Note that + the CanvasHelper does <em>not</em> take ownership of the + passed rDevice reference, nor does it perform any + reference counting. Thus, to prevent the reference counted + SpriteCanvas object from deletion, the user of this class + is responsible for holding ref-counted references itself! + + @param rDevice + Reference device this canvas is associated with + + */ + void init( ::com::sun::star::rendering::XGraphicDevice& rDevice, + SpriteDeviceHelper& rDeviceHelper ); + + // CanvasHelper functionality + // ========================== + + // XCanvas (only providing, not implementing the + // interface. Also note subtle method parameter differences) + void clear(); + void drawPoint( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::geometry::RealPoint2D& aPoint, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + void drawLine( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::geometry::RealPoint2D& aStartPoint, + const ::com::sun::star::geometry::RealPoint2D& aEndPoint, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + void drawBezier( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::geometry::RealBezierSegment2D& aBezierSegment, + const ::com::sun::star::geometry::RealPoint2D& aEndPoint, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + drawPolyPolygon( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + strokePolyPolygon( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::rendering::StrokeAttributes& strokeAttributes ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + strokeTexturedPolyPolygon( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures, + const ::com::sun::star::rendering::StrokeAttributes& strokeAttributes ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + strokeTextureMappedPolyPolygon( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures, + const ::com::sun::star::uno::Reference< + ::com::sun::star::geometry::XMapping2D >& xMapping, + const ::com::sun::star::rendering::StrokeAttributes& strokeAttributes ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D > + queryStrokeShapes( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::rendering::StrokeAttributes& strokeAttributes ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + fillPolyPolygon( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + fillTexturedPolyPolygon( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + fillTextureMappedPolyPolygon( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures, + const ::com::sun::star::uno::Reference< + ::com::sun::star::geometry::XMapping2D >& xMapping ); + + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCanvasFont > SAL_CALL + createFont( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::rendering::FontRequest& fontRequest, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::beans::PropertyValue >& extraFontProperties, + const ::com::sun::star::geometry::Matrix2D& fontMatrix ); + + ::com::sun::star::uno::Sequence< ::com::sun::star::rendering::FontInfo > + queryAvailableFonts( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::rendering::FontInfo& aFilter, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::beans::PropertyValue >& aFontProperties ); + + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + drawText( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::rendering::StringContext& text, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XCanvasFont >& xFont, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + sal_Int8 textDirection ); + + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + drawTextLayout( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XTextLayout >& layoutetText, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + drawBitmap( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XBitmap >& xBitmap, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCachedPrimitive > + drawBitmapModulated( const ::com::sun::star::rendering::XCanvas* pCanvas, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XBitmap >& xBitmap, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice > + getDevice(); + + // Flush drawing queue to screen + void flush() const; + + /** Called from XCanvas base classes, to notify that content + is _about_ to change + */ + void modifying() {} + + /** Write out recorded actions + */ + bool renderRecordedActions() const; + + /** Retrieve number of recorded actions + */ + size_t getRecordedActionCount() const; + + SpriteDeviceHelper* getDeviceHelper() const { return mpDeviceHelper; } + ::com::sun::star::rendering::XGraphicDevice* getDevice() const { return mpDevice; } + + struct Action; + typedef o3tl::cow_wrapper< std::vector<Action>, + o3tl::ThreadSafeRefCountingPolicy > RecordVectorT; + + private: + // declared, but not defined + CanvasHelper( const CanvasHelper& ); + + void setupGraphicsState( Action& o_action, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + /** Phyical output device + + Deliberately not a refcounted reference, because of + potential circular references for spritecanvas. + */ + ::com::sun::star::rendering::XGraphicDevice* mpDevice; + + /** Internal helper - used for a few global GL objects, + e.g. shader programs; and caches + */ + SpriteDeviceHelper* mpDeviceHelper; + + /** Ptr to array of recorded render calls + + Gets shared copy-on-write, when this CanvasHelper is + copied (used e.g. for CanvasBitmap) + */ + RecordVectorT mpRecordedActions; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvastools.cxx b/canvas/source/opengl/ogl_canvastools.cxx new file mode 100644 index 000000000000..a3a73dba820b --- /dev/null +++ b/canvas/source/opengl/ogl_canvastools.cxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#define GL_GLEXT_PROTOTYPES + +#include "ogl_canvastools.hxx" + +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/tools/tools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> + +#include <com/sun/star/rendering/ARGBColor.hpp> + +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glext.h> + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + /// triangulates polygon before + void renderComplexPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly ) + { + ::basegfx::B2DPolyPolygon aPolyPoly(rPolyPoly); + if( aPolyPoly.areControlPointsUsed() ) + aPolyPoly = rPolyPoly.getDefaultAdaptiveSubdivision(); + const ::basegfx::B2DRange& rBounds(aPolyPoly.getB2DRange()); + const double nWidth=rBounds.getWidth(); + const double nHeight=rBounds.getHeight(); + const ::basegfx::B2DPolygon& rTriangulatedPolygon( + ::basegfx::triangulator::triangulate(aPolyPoly)); + + for( sal_uInt32 i=0; i<rTriangulatedPolygon.count(); i++ ) + { + const ::basegfx::B2DPoint& rPt( rTriangulatedPolygon.getB2DPoint(i) ); + const double s(rPt.getX()/nWidth); + const double t(rPt.getY()/nHeight); + glTexCoord2f(s,t); glVertex2d(rPt.getX(), rPt.getY()); + } + } + + /** only use this for line polygons. + + better not leave triangulation to OpenGL. also, ignores texturing + */ + void renderPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly ) + { + ::basegfx::B2DPolyPolygon aPolyPoly(rPolyPoly); + if( aPolyPoly.areControlPointsUsed() ) + aPolyPoly = rPolyPoly.getDefaultAdaptiveSubdivision(); + + for( sal_uInt32 i=0; i<aPolyPoly.count(); i++ ) + { + glBegin(GL_LINE_STRIP); + + const ::basegfx::B2DPolygon& rPolygon( aPolyPoly.getB2DPolygon(i) ); + + const sal_uInt32 nPts=rPolygon.count(); + const sal_uInt32 nExtPts=nPts + rPolygon.isClosed(); + for( sal_uInt32 j=0; j<nExtPts; j++ ) + { + const ::basegfx::B2DPoint& rPt( rPolygon.getB2DPoint( j % nPts ) ); + glVertex2d(rPt.getX(), rPt.getY()); + } + + glEnd(); + } + } + + void setupState( const ::basegfx::B2DHomMatrix& rTransform, + GLenum eSrcBlend, + GLenum eDstBlend, + const rendering::ARGBColor& rColor ) + { + double aGLTransform[] = + { + rTransform.get(0,0), rTransform.get(1,0), 0, 0, + rTransform.get(0,1), rTransform.get(1,1), 0, 0, + 0, 0, 1, 0, + rTransform.get(0,2), rTransform.get(1,2), 0, 1 + }; + glMultMatrixd(aGLTransform); + + glEnable(GL_BLEND); + glBlendFunc(eSrcBlend, eDstBlend); + + glColor4d(rColor.Red, + rColor.Green, + rColor.Blue, + rColor.Alpha); + + // GL 1.2: + // glBlendEquation( GLenum mode ); + // glBlendColor( GLclampf red, GLclampf green,GLclampf blue, GLclampf alpha ); + // glConvolutionFilter1D + // glConvolutionFilter2D + // glSeparableFilter2D + } + + void renderOSD( const std::vector<double>& rNumbers, double scale ) + { + double y=4.0; + basegfx::B2DHomMatrix aTmp; + basegfx::B2DHomMatrix aScaleShear; + aScaleShear.shearX(-0.1); + aScaleShear.scale(scale,scale); + + for( size_t i=0; i<rNumbers.size(); ++i ) + { + aTmp.identity(); + aTmp.translate(0,y); + y += 1.2*scale; + + basegfx::B2DPolyPolygon aPoly= + basegfx::tools::number2PolyPolygon(rNumbers[i],10,3); + + aTmp=aTmp*aScaleShear; + aPoly.transform(aTmp); + + glColor4f(0,1,0,1); + renderPolyPolygon(aPoly); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_canvastools.hxx b/canvas/source/opengl/ogl_canvastools.hxx new file mode 100644 index 000000000000..458bddd1d1e6 --- /dev/null +++ b/canvas/source/opengl/ogl_canvastools.hxx @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_CANVASTOOLS_HXX +#define OGL_CANVASTOOLS_HXX + +#include <sal/config.h> +#include <vector> + +namespace com { namespace sun { namespace star { namespace rendering { + struct ARGBColor; +}}}} +namespace basegfx { + class B2DPolyPolygon; + class B2DHomMatrix; +} + +namespace oglcanvas +{ + void renderComplexPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly ); + void renderPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPoly ); + void setupState( const ::basegfx::B2DHomMatrix& rTransform, + unsigned int eSrcBlend, + unsigned int eDstBlend, + const ::com::sun::star::rendering::ARGBColor& rColor ); + + void renderOSD( const std::vector<double>& rNumbers, double scale ); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_spritecanvas.cxx b/canvas/source/opengl/ogl_spritecanvas.cxx new file mode 100644 index 000000000000..a3d114bbbabb --- /dev/null +++ b/canvas/source/opengl/ogl_spritecanvas.cxx @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_spritecanvas.hxx" + +#include <canvas/debug.hxx> +#include <canvas/verbosetrace.hxx> +#include <tools/diagnose_ex.h> + +#include <osl/mutex.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/NoSupportException.hpp> + +#include <toolkit/helper/vclunohelper.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implementationentry.hxx> +#include <comphelper/servicedecl.hxx> + +#include "ogl_canvascustomsprite.hxx" + +#include <GL/gl.h> +#include <GL/glext.h> + +#define SPRITECANVAS_SERVICE_NAME "com.sun.star.rendering.SpriteCanvas.OGL" +#define SPRITECANVAS_IMPLEMENTATION_NAME "com.sun.star.comp.rendering.SpriteCanvas.OGL" + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + SpriteCanvas::SpriteCanvas( const uno::Sequence< uno::Any >& aArguments, + const uno::Reference< uno::XComponentContext >& rxContext ) : + maArguments(aArguments), + mxComponentContext( rxContext ) + { + } + + void SpriteCanvas::initialize() + { + // Only call initialize when not in probe mode + if( maArguments.getLength() == 0 ) + return; + + VERBOSE_TRACE( "SpriteCanvas::initialize called" ); + + /* aArguments: + 0: ptr to creating instance (Window or VirtualDevice) + 1: SystemEnvData as a streamed Any (or empty for VirtualDevice) + 2: current bounds of creating instance + 3: bool, denoting always on top state for Window (always false for VirtualDevice) + 4: XWindow for creating Window (or empty for VirtualDevice) + 5: SystemGraphicsData as a streamed Any + */ + ENSURE_ARG_OR_THROW( maArguments.getLength() >= 5 && + maArguments[4].getValueTypeClass() == uno::TypeClass_INTERFACE, + "OpenGL SpriteCanvas::initialize: wrong number of arguments, or wrong types" ); + + uno::Reference< awt::XWindow > xParentWindow; + maArguments[4] >>= xParentWindow; + Window* pParentWindow = VCLUnoHelper::GetWindow(xParentWindow); + if( !pParentWindow ) + throw lang::NoSupportException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "Parent window not VCL window, or canvas out-of-process!")), + NULL); + + awt::Rectangle aRect; + maArguments[2] >>= aRect; + + // setup helper + maDeviceHelper.init( *pParentWindow, + *this, + aRect ); + maCanvasHelper.init( *this, maDeviceHelper ); + maArguments.realloc(0); + } + + void SAL_CALL SpriteCanvas::disposeThis() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + mxComponentContext.clear(); + + // forward to parent + SpriteCanvasBaseT::disposeThis(); + } + + ::sal_Bool SAL_CALL SpriteCanvas::showBuffer( ::sal_Bool bUpdateAll ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // avoid repaints on hidden window (hidden: not mapped to + // screen). Return failure, since the screen really has _not_ + // been updated (caller should try again later) + return !mbIsVisible ? false : SpriteCanvasBaseT::showBuffer( bUpdateAll ); + } + + ::sal_Bool SAL_CALL SpriteCanvas::switchBuffer( ::sal_Bool bUpdateAll ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // avoid repaints on hidden window (hidden: not mapped to + // screen). Return failure, since the screen really has _not_ + // been updated (caller should try again later) + return !mbIsVisible ? false : SpriteCanvasBaseT::switchBuffer( bUpdateAll ); + } + + uno::Reference< rendering::XAnimatedSprite > SAL_CALL SpriteCanvas::createSpriteFromAnimation( + const uno::Reference< rendering::XAnimation >& /*animation*/ ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + return uno::Reference< rendering::XAnimatedSprite >(); + } + + uno::Reference< rendering::XAnimatedSprite > SAL_CALL SpriteCanvas::createSpriteFromBitmaps( + const uno::Sequence< uno::Reference< rendering::XBitmap > >& /*animationBitmaps*/, + ::sal_Int8 /*interpolationMode*/ ) throw (lang::IllegalArgumentException, + rendering::VolatileContentDestroyedException, + uno::RuntimeException) + { + return uno::Reference< rendering::XAnimatedSprite >(); + } + + uno::Reference< rendering::XCustomSprite > SAL_CALL SpriteCanvas::createCustomSprite( + const geometry::RealSize2D& spriteSize ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + return uno::Reference< rendering::XCustomSprite >( + new CanvasCustomSprite(spriteSize, this, maDeviceHelper) ); + } + + uno::Reference< rendering::XSprite > SAL_CALL SpriteCanvas::createClonedSprite( + const uno::Reference< rendering::XSprite >& /*original*/ ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + return uno::Reference< rendering::XSprite >(); + } + + sal_Bool SAL_CALL SpriteCanvas::updateScreen( sal_Bool bUpdateAll ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return maDeviceHelper.showBuffer(mbIsVisible, bUpdateAll); + } + + ::rtl::OUString SAL_CALL SpriteCanvas::getServiceName( ) throw (uno::RuntimeException) + { + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SPRITECANVAS_SERVICE_NAME ) ); + } + + void SpriteCanvas::show( const ::rtl::Reference< CanvasCustomSprite >& xSprite ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + maDeviceHelper.show(xSprite); + } + + void SpriteCanvas::hide( const ::rtl::Reference< CanvasCustomSprite >& xSprite ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + maDeviceHelper.hide(xSprite); + } + + bool SpriteCanvas::renderRecordedActions() const + { + return maCanvasHelper.renderRecordedActions(); + } + + static uno::Reference<uno::XInterface> initCanvas( SpriteCanvas* pCanvas ) + { + uno::Reference<uno::XInterface> xRet(static_cast<cppu::OWeakObject*>(pCanvas)); + pCanvas->initialize(); + return xRet; + } + + namespace sdecl = comphelper::service_decl; + sdecl::class_<SpriteCanvas, sdecl::with_args<true> > serviceImpl(&initCanvas); + const sdecl::ServiceDecl oglSpriteCanvasDecl( + serviceImpl, + SPRITECANVAS_IMPLEMENTATION_NAME, + SPRITECANVAS_SERVICE_NAME ); +} + +// The C shared lib entry points +COMPHELPER_SERVICEDECL_EXPORTS1(oglcanvas, oglcanvas::oglSpriteCanvasDecl); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_spritecanvas.hxx b/canvas/source/opengl/ogl_spritecanvas.hxx new file mode 100644 index 000000000000..6efc6dbe8fb1 --- /dev/null +++ b/canvas/source/opengl/ogl_spritecanvas.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_SPRITECANVAS_HXX_ +#define OGL_SPRITECANVAS_HXX_ + +#include <rtl/ref.hxx> + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceName.hpp> +#include <com/sun/star/awt/XWindowListener.hpp> +#include <com/sun/star/util/XUpdatable.hpp> +#include <com/sun/star/rendering/XSpriteCanvas.hpp> +#include <com/sun/star/rendering/XGraphicDevice.hpp> +#include <com/sun/star/rendering/XBufferController.hpp> + +#include <cppuhelper/compbase8.hxx> +#include <comphelper/uno3.hxx> + +#include <canvas/base/spritecanvasbase.hxx> +#include <canvas/base/disambiguationhelper.hxx> +#include <canvas/base/bufferedgraphicdevicebase.hxx> + +#include "ogl_spritedevicehelper.hxx" +#include "ogl_canvashelper.hxx" + + +namespace oglcanvas +{ + class CanvasCustomSprite; + + typedef ::cppu::WeakComponentImplHelper8< ::com::sun::star::rendering::XSpriteCanvas, + ::com::sun::star::rendering::XGraphicDevice, + ::com::sun::star::lang::XMultiServiceFactory, + ::com::sun::star::rendering::XBufferController, + ::com::sun::star::awt::XWindowListener, + ::com::sun::star::util::XUpdatable, + ::com::sun::star::beans::XPropertySet, + ::com::sun::star::lang::XServiceName > WindowGraphicDeviceBase_Base; + typedef ::canvas::BufferedGraphicDeviceBase< + ::canvas::DisambiguationHelper< WindowGraphicDeviceBase_Base >, + SpriteDeviceHelper, + ::osl::MutexGuard, + ::cppu::OWeakObject > SpriteCanvasDeviceBaseT; + + typedef ::canvas::CanvasBase< SpriteCanvasDeviceBaseT, + CanvasHelper, + ::osl::MutexGuard, + ::cppu::OWeakObject > SpriteCanvasBaseT; + + /** Product of this component's factory. + + The SpriteCanvas object combines the actual Window canvas with + the XGraphicDevice interface. This is because there's a + one-to-one relation between them, anyway, since each window + can have exactly one canvas and one associated + XGraphicDevice. And to avoid messing around with circular + references, this is implemented as one single object. + */ + class SpriteCanvas : public SpriteCanvasBaseT + { + public: + SpriteCanvas( const ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Any >& aArguments, + const ::com::sun::star::uno::Reference< + ::com::sun::star::uno::XComponentContext >& rxContext ); + + void initialize(); + + /// Dispose all internal references + virtual void disposeThis(); + + // Forwarding the XComponent implementation to the + // cppu::ImplHelper templated base + // Classname Base doing refcounting Base implementing the XComponent interface + // | | | + // V V V + DECLARE_UNO3_XCOMPONENT_AGG_DEFAULTS( SpriteCanvas, WindowGraphicDeviceBase_Base, ::cppu::WeakComponentImplHelperBase ); + + // XBufferController (partial) + virtual ::sal_Bool SAL_CALL showBuffer( ::sal_Bool bUpdateAll ) throw (::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL switchBuffer( ::sal_Bool bUpdateAll ) throw (::com::sun::star::uno::RuntimeException); + + // XSpriteCanvas + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XAnimatedSprite > SAL_CALL createSpriteFromAnimation( const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XAnimation >& animation ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XAnimatedSprite > SAL_CALL createSpriteFromBitmaps( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmap > >& animationBitmaps, ::sal_Int8 interpolationMode ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::rendering::VolatileContentDestroyedException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCustomSprite > SAL_CALL createCustomSprite( const ::com::sun::star::geometry::RealSize2D& spriteSize ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XSprite > SAL_CALL createClonedSprite( const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XSprite >& original ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::sal_Bool SAL_CALL updateScreen( ::sal_Bool bUpdateAll ) throw (::com::sun::star::uno::RuntimeException); + + // XServiceName + virtual ::rtl::OUString SAL_CALL getServiceName( ) throw (::com::sun::star::uno::RuntimeException); + + void show( const ::rtl::Reference< CanvasCustomSprite >& ); + void hide( const ::rtl::Reference< CanvasCustomSprite >& ); + + /** Write out recorded actions + */ + bool renderRecordedActions() const; + + private: + ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > maArguments; + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext > mxComponentContext; + }; + + typedef ::rtl::Reference< SpriteCanvas > SpriteCanvasRef; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_spritedevicehelper.cxx b/canvas/source/opengl/ogl_spritedevicehelper.cxx new file mode 100644 index 000000000000..cb96f2e6cf94 --- /dev/null +++ b/canvas/source/opengl/ogl_spritedevicehelper.cxx @@ -0,0 +1,1249 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_spritedevicehelper.hxx" +#include "ogl_spritecanvas.hxx" +#include "ogl_canvasbitmap.hxx" +#include "ogl_canvastools.hxx" +#include "ogl_canvascustomsprite.hxx" +#include "ogl_texturecache.hxx" + +#include <canvas/verbosetrace.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/tools/unopolypolygon.hxx> + +#include <osl/mutex.hxx> +#include <rtl/instance.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/lang/NoSupportException.hpp> +#include <com/sun/star/rendering/XColorSpace.hpp> +#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp> + +#include <vcl/sysdata.hxx> +#include <vcl/syschild.hxx> +#include <vcl/canvastools.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#define GL_GLEXT_PROTOTYPES +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glext.h> + +namespace unx +{ + #include <X11/keysym.h> + #include <X11/X.h> + #include <GL/glx.h> + #include <GL/glxext.h> +} + + +using namespace ::com::sun::star; + +static bool lcl_bErrorTriggered=false; +static int lcl_XErrorHandler( unx::Display*, unx::XErrorEvent* ) +{ + lcl_bErrorTriggered = true; + return 0; +} + +/** Dummy vertex processing. Simply uses default pipeline for vertex + transformation, and forwards texture coodinates to fragment shader + */ +static const char dummyVertexShader[] = +{ + "varying vec2 v_textureCoords2d; " + "void main(void) " + "{ " + " gl_Position = ftransform(); " + " v_textureCoords2d = gl_MultiTexCoord0.st; " + "} " +}; + +/** Two-color linear gradient + */ +static const char linearTwoColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform vec4 v_startColor4d; " + "uniform vec4 v_endColor4d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "void main(void) " + "{ " + " gl_FragColor = mix(v_startColor4d, " + " v_endColor4d, " + " clamp( " + " (m_transform * vec3(v_textureCoords2d,1)).s, " + " 0.0, 1.0)); " + "} " +}; + +/** N-color linear gradient + */ +static const char linearMultiColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform int i_nColors; " + "uniform sampler1D t_colorArray4d; " + "uniform sampler1D t_stopArray1d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + " " + "int findBucket(float t) " + "{ " + " int nMinBucket=0; " + " while( nMinBucket < i_nColors && " + " texture1D(t_stopArray1d, nMinBucket).s < t ) " + " ++nMinBucket; " + " return max(nMinBucket-1,0); " + "} " + " " + "void main(void) " + "{ " + " const float fAlpha = " + " clamp( (m_transform * vec3(v_textureCoords2d,1)).s, " + " 0.0, 1.0 ); " + " " + " const int nMinBucket=findBucket( fAlpha ); " + " " + " const float fLerp = " + " (fAlpha-texture1D(t_stopArray1d, nMinBucket).s) / " + " (texture1D(t_stopArray1d, nMinBucket+1).s - " + " texture1D(t_stopArray1d, nMinBucket).s); " + " " + " gl_FragColor = mix(texture1D(t_colorArray4d, nMinBucket), " + " texture1D(t_colorArray4d, nMinBucket+1), " + " fLerp); " + "} " +}; + +/** Two-color radial gradient + */ +static const char radialTwoColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform vec4 v_startColor4d; " + "uniform vec4 v_endColor4d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "const vec2 v_center2d = vec2(0,0); " + "void main(void) " + "{ " + " gl_FragColor = mix(v_startColor4d, " + " v_endColor4d, " + " 1.0 - distance( " + " vec2( " + " m_transform * vec3(v_textureCoords2d,1)), " + " v_center2d)); " + "} " +}; + +/** Multi-color radial gradient + */ +static const char radialMultiColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform int i_nColors; " + "uniform sampler1D t_colorArray4d; " + "uniform sampler1D t_stopArray1d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "const vec2 v_center2d = vec2(0,0); " + " " + "int findBucket(float t) " + "{ " + " int nMinBucket=0; " + " while( nMinBucket < i_nColors && " + " texture1D(t_stopArray1d, nMinBucket).s < t ) " + " ++nMinBucket; " + " return max(nMinBucket-1,0); " + "} " + " " + "void main(void) " + "{ " + " const float fAlpha = " + " clamp( 1.0 - distance( " + " vec2( m_transform * vec3(v_textureCoords2d,1)), " + " v_center2d), " + " 0.0, 1.0 ); " + " " + " const int nMinBucket=findBucket( fAlpha ); " + " " + " const float fLerp = " + " (fAlpha-texture1D(t_stopArray1d, nMinBucket).s) / " + " (texture1D(t_stopArray1d, nMinBucket+1).s - " + " texture1D(t_stopArray1d, nMinBucket).s); " + " " + " gl_FragColor = mix(texture1D(t_colorArray4d, nMinBucket), " + " texture1D(t_colorArray4d, nMinBucket+1), " + " fLerp); " + "} " +}; + +/** Two-color rectangular gradient + */ +static const char rectangularTwoColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform vec4 v_startColor4d; " + "uniform vec4 v_endColor4d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + "void main(void) " + "{ " + " const vec2 v = abs( vec2(m_transform * vec3(v_textureCoords2d,1)) ); " + " const float t = max(v.x, v.y); " + " gl_FragColor = mix(v_startColor4d, " + " v_endColor4d, " + " 1.0-t); " + "} " +}; + +/** Multi-color rectangular gradient + */ +static const char rectangularMultiColorGradientFragmentShader[] = +{ + "#version 120 \n" + "uniform int i_nColors; " + "uniform sampler1D t_colorArray4d; " + "uniform sampler1D t_stopArray1d; " + "uniform mat3x2 m_transform; " + "varying vec2 v_textureCoords2d; " + " " + "int findBucket(float t) " + "{ " + " int nMinBucket=0; " + " while( nMinBucket < i_nColors && " + " texture1D(t_stopArray1d, nMinBucket).s < t ) " + " ++nMinBucket; " + " return max(nMinBucket-1,0); " + "} " + " " + "void main(void) " + "{ " + " const vec2 v = abs( vec2(m_transform * vec3(v_textureCoords2d,1)) ); " + " const float fAlpha = 1 - max(v.x, v.y); " + " " + " const int nMinBucket=findBucket( fAlpha ); " + " " + " const float fLerp = " + " (fAlpha-texture1D(t_stopArray1d, nMinBucket).s) / " + " (texture1D(t_stopArray1d, nMinBucket+1).s - " + " texture1D(t_stopArray1d, nMinBucket).s); " + " " + " gl_FragColor = mix(texture1D(t_colorArray4d, nMinBucket), " + " texture1D(t_colorArray4d, nMinBucket+1), " + " fLerp); " + "} " +}; + +static void initContext() +{ + // need the backside for mirror effects + glDisable(GL_CULL_FACE); + + // no perspective, we're 2D + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // misc preferences + glEnable(GL_POINT_SMOOTH); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT,GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST); + glShadeModel(GL_FLAT); +} + +static void initTransformation(const ::Size& rSize, bool bMirror=false) +{ + // use whole window + glViewport( 0,0, + (GLsizei)rSize.Width(), + (GLsizei)rSize.Height() ); + + // model coordinate system is already in device pixel + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslated(-1.0, (bMirror ? -1.0 : 1.0), 0.0); + glScaled( 2.0 / rSize.Width(), + (bMirror ? 2.0 : -2.0) / rSize.Height(), + 1.0 ); + + // clear to black + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +static boost::shared_ptr<SystemChildWindow> createChildWindow( unx::XVisualInfo*& viWin, + unx::XVisualInfo*& viPB, + void*& fbConfig, + Window& rWindow, + unx::Display* pDisplay, + int nScreen ) +{ + // select appropriate visual + static int winAttrList3[] = + { + GLX_RGBA,//only TrueColor or DirectColor + //single buffered + GLX_RED_SIZE,4,//use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,//use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,//use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,0,//no depth buffer + None + }; + static int pBufAttrList3[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,//use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,//use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,//use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,0,//no depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int winAttrList2[] = + { + GLX_RGBA,//only TrueColor or DirectColor + /// single buffered + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,1,/// use the maximum depth bits, making sure there is a depth buffer + None + }; + static int pBufAttrList2[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,1,/// use the maximum depth bits, making sure there is a depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int winAttrList1[] = + { + GLX_RGBA,//only TrueColor or DirectColor + GLX_DOUBLEBUFFER,/// only double buffer + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,0,/// no depth buffer + None + }; + static int pBufAttrList1[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,/// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,/// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,/// use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,0,/// no depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int winAttrList0[] = + { + GLX_RGBA,//only TrueColor or DirectColor + GLX_DOUBLEBUFFER,// only double buffer + GLX_RED_SIZE,4,// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,// use the maximum blue bits, with a minimum of 4 bits + GLX_DEPTH_SIZE,1,// use the maximum depth bits, making sure there is a depth buffer + None + }; + static int pBufAttrList0[] = + { + GLX_DOUBLEBUFFER,False,// never doublebuffer pbuffer + GLX_RED_SIZE,4,// use the maximum red bits, with a minimum of 4 bits + GLX_GREEN_SIZE,4,// use the maximum green bits, with a minimum of 4 bits + GLX_BLUE_SIZE,4,// use the maximum blue bits, with a minimum of 4 bits + GLX_ALPHA_SIZE,4, + GLX_DEPTH_SIZE,1,// use the maximum depth bits, making sure there is a depth buffer + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT, + None + }; + static int* winAttrTable[] = + { + winAttrList0, + winAttrList1, + winAttrList2, + winAttrList3, + NULL + }; + static int* pBufAttrTable[] = + { + pBufAttrList0, + pBufAttrList1, + pBufAttrList2, + pBufAttrList3, + NULL + }; + int** pWinAttributeTable = winAttrTable; + int** pBufAttributeTable = pBufAttrTable; + + boost::shared_ptr<SystemChildWindow> pResult; + unx::GLXFBConfig* fbConfigs=NULL; + int nConfigs, nVal; + while( *pWinAttributeTable && *pBufAttributeTable ) + { + // try to find a window visual for the current set of + // attributes + viWin = unx::glXChooseVisual( pDisplay, + nScreen, + *pWinAttributeTable ); + if( viWin ) + { + // try to find a framebuffer config for the current set of + // attributes + fbConfigs = glXChooseFBConfig( pDisplay, + nScreen, + *pBufAttributeTable, + &nConfigs ); + // don't use glXGetFBConfigs, that does not list alpha-configs + // fbConfigs = unx::glXGetFBConfigs(pDisplay, nScreen, &nConfigs); + for(int i=0; i<nConfigs; i++) + { + viPB = glXGetVisualFromFBConfig(pDisplay, fbConfigs[i]); + if( viPB && viPB->visualid != viWin->visualid ) + { + glXGetFBConfigAttrib(pDisplay, + fbConfigs[i], + GLX_DRAWABLE_TYPE, + &nVal); + + if( (GLX_PBUFFER_BIT|GLX_WINDOW_BIT|GLX_PIXMAP_BIT) + == (nVal & (GLX_PBUFFER_BIT|GLX_WINDOW_BIT|GLX_PIXMAP_BIT)) ) + { + SystemWindowData winData; + winData.nSize = sizeof(winData); + SAL_INFO("canvas.ogl", "using VisualID " << viWin->visualid << " for OpenGL canvas"); + winData.pVisual = (void*)(viWin->visual); + pResult.reset( new SystemChildWindow(&rWindow, 0, &winData, false) ); + + if( pResult->GetSystemData() ) + { + fbConfig = &fbConfigs[i]; + return pResult; + } + + pResult.reset(); + } + + XFree(viPB); + } + } + + XFree(viWin); + } + + ++pWinAttributeTable; + ++pBufAttributeTable; + } + + return pResult; +} + + +namespace oglcanvas +{ + /** Compile shader program + + Code courtesy rodo + */ + void SpriteDeviceHelper::compileShader(unsigned int& o_rShaderHandle, + unsigned int eShaderType, + const char* pShaderSourceCode) + { + GLint nCompileStatus; + char log[1024]; + + o_rShaderHandle = glCreateShader( eShaderType ); + glShaderSource( o_rShaderHandle, 1, &pShaderSourceCode, NULL ); + glCompileShader( o_rShaderHandle ); + glGetShaderInfoLog( o_rShaderHandle, sizeof(log), NULL, log ); + SAL_INFO("canvas.ogl", "shader compile log: " << log); + + glGetShaderiv( o_rShaderHandle, GL_COMPILE_STATUS, &nCompileStatus ); + if( !nCompileStatus ) + { + glDeleteShader(o_rShaderHandle); + o_rShaderHandle=0; + } + } + + /** Link vertex & fragment shaders + + Code courtesy rodo + */ + void SpriteDeviceHelper::linkShaders(unsigned int& o_rProgramHandle, + unsigned int nVertexProgramId, + unsigned int nFragmentProgramId) + { + if( !nVertexProgramId || !nFragmentProgramId ) + return; + + o_rProgramHandle = glCreateProgram(); + glAttachShader( o_rProgramHandle, nVertexProgramId ); + glAttachShader( o_rProgramHandle, nFragmentProgramId ); + + char log[1024]; + GLint nProgramLinked; + + glLinkProgram( o_rProgramHandle ); + glGetProgramInfoLog( o_rProgramHandle, sizeof(log), NULL, log ); + SAL_INFO("canvas.ogl", "shader program link log: " << log); + glGetProgramiv( o_rProgramHandle, GL_LINK_STATUS, &nProgramLinked ); + + if( !nProgramLinked ) + { + glDeleteProgram(o_rProgramHandle); + o_rProgramHandle=0; + } + } + + SpriteDeviceHelper::SpriteDeviceHelper() : + mpDevice(NULL), + mpSpriteCanvas(NULL), + maActiveSprites(), + maLastUpdate(), + mpChildWindow(), + mpDisplay(NULL), + mpGLContext(NULL), + mpGLPBufContext(NULL), + mpFBConfig(NULL), + mpTextureCache(new TextureCache()), + mnDummyVertexProgram(0), + mnLinearTwoColorGradientFragmentProgram(0), + mnLinearMultiColorGradientFragmentProgram(0), + mnRadialTwoColorGradientFragmentProgram(0), + mnRadialMultiColorGradientFragmentProgram(0), + mnRectangularTwoColorGradientFragmentProgram(0), + mnRectangularMultiColorGradientFragmentProgram(0), + mnLinearTwoColorGradientProgram(0), + mnLinearMultiColorGradientProgram(0), + mnRadialTwoColorGradientProgram(0), + mnRadialMultiColorGradientProgram(0), + mnRectangularTwoColorGradientProgram(0), + mnRectangularMultiColorGradientProgram(0) + {} + + void SpriteDeviceHelper::init( Window& rWindow, + SpriteCanvas& rSpriteCanvas, + const awt::Rectangle& rViewArea ) + { + mpSpriteCanvas = &rSpriteCanvas; + + rSpriteCanvas.setWindow( + uno::Reference<awt::XWindow2>( + VCLUnoHelper::GetInterface(&rWindow), + uno::UNO_QUERY_THROW) ); + + // init OpenGL + const SystemEnvData* sysData(rWindow.GetSystemData()); + unx::Display* pDisplay=reinterpret_cast<unx::Display*>(sysData->pDisplay); + mpDisplay=pDisplay; + if( !unx::glXQueryExtension(pDisplay, NULL, NULL) ) + return; + + unx::Window xWindow = sysData->aWindow; + unx::XWindowAttributes xAttr; + unx::XGetWindowAttributes( pDisplay, xWindow, &xAttr ); + int nScreen = XScreenNumberOfScreen( xAttr.screen ); + + unx::Window childXWindow=0; + unx::XVisualInfo* viWin=NULL; + unx::XVisualInfo* viPB=NULL; + mpChildWindow=createChildWindow(viWin,viPB,mpFBConfig, + rWindow,pDisplay,nScreen); + + // tweak SysChild window to act as an input-transparent + // overlay + if( mpChildWindow ) + { + childXWindow=mpChildWindow->GetSystemData()->aWindow; + mpChildWindow->SetMouseTransparent(true); + mpChildWindow->SetParentClipMode( PARENTCLIPMODE_NOCLIP ); + mpChildWindow->EnableEraseBackground(false); + mpChildWindow->SetControlForeground(); + mpChildWindow->SetControlBackground(); + mpChildWindow->EnablePaint(false); + + unx::GLXContext pContext1 = + glXCreateContext(pDisplay, + viWin, + 0, + GL_TRUE); + mpGLContext = pContext1; + + unx::GLXContext pContext2 = + glXCreateContext( pDisplay, + viPB, + pContext1, + GL_TRUE ); + mpGLPBufContext = pContext2; + + XFree(viWin); + XFree(viPB); + + if( !glXMakeCurrent( pDisplay, + childXWindow, + pContext1) ) + { + glXDestroyContext(pDisplay, pContext1); + glXDestroyContext(pDisplay, pContext2); + throw lang::NoSupportException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "Could not select OpenGL context!") ),NULL); + } + + const GLubyte* extensions=glGetString( GL_EXTENSIONS ); + if( gluCheckExtension((const GLubyte*)"GLX_SGI_swap_control", extensions) ) + { + // try to enable vsync + typedef GLint (*glXSwapIntervalProc)(GLint); + glXSwapIntervalProc glXSwapInterval = + (glXSwapIntervalProc) unx::glXGetProcAddress((const GLubyte*)"glXSwapIntervalSGI"); + if( glXSwapInterval ) + { + int (*oldHandler)(unx::Display*, unx::XErrorEvent*); + + // synchronize on global mutex - no other ogl + // canvas instance permitted to enter here + { + ::osl::MutexGuard aGuard( *::osl::Mutex::getGlobalMutex() ); + + // replace error handler temporarily + oldHandler = unx::XSetErrorHandler( lcl_XErrorHandler ); + + lcl_bErrorTriggered = false; + + // Note: if this fails, so be it. Buggy + // drivers will then not have vsync. + glXSwapInterval(1); + + // sync so that we possibly get an XError + unx::glXWaitGL(); + XSync(pDisplay, false); + + unx::XSetErrorHandler( oldHandler ); + } + } + } + + // init window context + initContext(); + + // compile & link shaders - code courtesy rodo + compileShader(mnDummyVertexProgram, + GL_VERTEX_SHADER, + dummyVertexShader); + compileShader(mnLinearTwoColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + linearTwoColorGradientFragmentShader); + compileShader(mnLinearMultiColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + linearMultiColorGradientFragmentShader); + compileShader(mnRadialTwoColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + radialTwoColorGradientFragmentShader); + compileShader(mnRadialMultiColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + radialMultiColorGradientFragmentShader); + compileShader(mnRectangularTwoColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + rectangularTwoColorGradientFragmentShader); + compileShader(mnRectangularMultiColorGradientFragmentProgram, + GL_FRAGMENT_SHADER, + rectangularMultiColorGradientFragmentShader); + linkShaders(mnLinearTwoColorGradientProgram, + mnDummyVertexProgram, + mnLinearTwoColorGradientFragmentProgram); + linkShaders(mnLinearMultiColorGradientProgram, + mnDummyVertexProgram, + mnLinearMultiColorGradientFragmentProgram); + linkShaders(mnRadialTwoColorGradientProgram, + mnDummyVertexProgram, + mnRadialTwoColorGradientFragmentProgram); + linkShaders(mnRadialMultiColorGradientProgram, + mnDummyVertexProgram, + mnRadialMultiColorGradientFragmentProgram); + linkShaders(mnRectangularTwoColorGradientProgram, + mnDummyVertexProgram, + mnRectangularTwoColorGradientFragmentProgram); + linkShaders(mnRectangularMultiColorGradientProgram, + mnDummyVertexProgram, + mnRectangularMultiColorGradientFragmentProgram); + + glXMakeCurrent(pDisplay, None, NULL); + } + + if( !mpGLContext || glGetError() != GL_NO_ERROR ) + throw lang::NoSupportException( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "Could not create OpenGL context, or an error occurred doing so!") ),NULL); + + notifySizeUpdate(rViewArea); + mpChildWindow->Show(); + // TODO(E3): check for GL_ARB_imaging extension + } + + void SpriteDeviceHelper::disposing() + { + // release all references + mpSpriteCanvas = NULL; + mpDevice = NULL; + mpTextureCache.reset(); + + if( mpGLContext ) + { + glDeleteProgram( mnRectangularTwoColorGradientProgram ); + glDeleteProgram( mnRectangularMultiColorGradientProgram ); + glDeleteProgram( mnRadialTwoColorGradientProgram ); + glDeleteProgram( mnRadialMultiColorGradientProgram ); + glDeleteProgram( mnLinearTwoColorGradientProgram ); + glDeleteProgram( mnLinearMultiColorGradientProgram ); + glDeleteShader( mnRectangularTwoColorGradientFragmentProgram ); + glDeleteShader( mnRectangularMultiColorGradientFragmentProgram ); + glDeleteShader( mnRadialTwoColorGradientFragmentProgram ); + glDeleteShader( mnRadialMultiColorGradientFragmentProgram ); + glDeleteShader( mnLinearTwoColorGradientFragmentProgram ); + glDeleteShader( mnLinearMultiColorGradientFragmentProgram ); + glDeleteShader( mnDummyVertexProgram ); + + glXDestroyContext(reinterpret_cast<unx::Display*>(mpDisplay), + reinterpret_cast<unx::GLXContext>(mpGLContext)); + } + + mpDisplay = NULL; + mpGLContext = NULL; + mpChildWindow.reset(); + } + + geometry::RealSize2D SpriteDeviceHelper::getPhysicalResolution() + { + if( !mpChildWindow ) + return ::canvas::tools::createInfiniteSize2D(); // we're disposed + + // Map a one-by-one millimeter box to pixel + const MapMode aOldMapMode( mpChildWindow->GetMapMode() ); + mpChildWindow->SetMapMode( MapMode(MAP_MM) ); + const Size aPixelSize( mpChildWindow->LogicToPixel(Size(1,1)) ); + mpChildWindow->SetMapMode( aOldMapMode ); + + return ::vcl::unotools::size2DFromSize( aPixelSize ); + } + + geometry::RealSize2D SpriteDeviceHelper::getPhysicalSize() + { + if( !mpChildWindow ) + return ::canvas::tools::createInfiniteSize2D(); // we're disposed + + // Map the pixel dimensions of the output window to millimeter + const MapMode aOldMapMode( mpChildWindow->GetMapMode() ); + mpChildWindow->SetMapMode( MapMode(MAP_MM) ); + const Size aLogSize( mpChildWindow->PixelToLogic(mpChildWindow->GetOutputSizePixel()) ); + mpChildWindow->SetMapMode( aOldMapMode ); + + return ::vcl::unotools::size2DFromSize( aLogSize ); + } + + uno::Reference< rendering::XLinePolyPolygon2D > SpriteDeviceHelper::createCompatibleLinePolyPolygon( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const uno::Sequence< uno::Sequence< geometry::RealPoint2D > >& points ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XLinePolyPolygon2D >(); // we're disposed + + return uno::Reference< rendering::XLinePolyPolygon2D >( + new ::basegfx::unotools::UnoPolyPolygon( + ::basegfx::unotools::polyPolygonFromPoint2DSequenceSequence( points ))); + } + + uno::Reference< rendering::XBezierPolyPolygon2D > SpriteDeviceHelper::createCompatibleBezierPolyPolygon( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const uno::Sequence< uno::Sequence< geometry::RealBezierSegment2D > >& points ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XBezierPolyPolygon2D >(); // we're disposed + + return uno::Reference< rendering::XBezierPolyPolygon2D >( + new ::basegfx::unotools::UnoPolyPolygon( + ::basegfx::unotools::polyPolygonFromBezier2DSequenceSequence( points ) ) ); + } + + uno::Reference< rendering::XBitmap > SpriteDeviceHelper::createCompatibleBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& size ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XBitmap >(); // we're disposed + + return uno::Reference< rendering::XBitmap >( + new CanvasBitmap( size, + mpSpriteCanvas, + *this, + false ) ); + } + + uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& /*size*/ ) + { + return uno::Reference< rendering::XVolatileBitmap >(); + } + + uno::Reference< rendering::XBitmap > SpriteDeviceHelper::createCompatibleAlphaBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& size ) + { + // disposed? + if( !mpSpriteCanvas ) + return uno::Reference< rendering::XBitmap >(); // we're disposed + + return uno::Reference< rendering::XBitmap >( + new CanvasBitmap( size, + mpSpriteCanvas, + *this, + true ) ); + } + + uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileAlphaBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& /*size*/ ) + { + return uno::Reference< rendering::XVolatileBitmap >(); + } + + sal_Bool SpriteDeviceHelper::hasFullScreenMode() + { + // TODO(F3): offer fullscreen mode the XCanvas way + return false; + } + + sal_Bool SpriteDeviceHelper::enterFullScreenMode( sal_Bool /*bEnter*/ ) + { + // TODO(F3): offer fullscreen mode the XCanvas way + return false; + } + + ::sal_Int32 SpriteDeviceHelper::createBuffers( ::sal_Int32 /*nBuffers*/ ) + { + // TODO(F3): implement XBufferStrategy interface. For now, we + // _always_ will have exactly one backbuffer + return 1; + } + + void SpriteDeviceHelper::destroyBuffers() + { + // TODO(F3): implement XBufferStrategy interface. For now, we + // _always_ will have exactly one backbuffer + } + + namespace + { + /** Functor providing a StrictWeakOrdering for XSprites (over + priority) + */ + struct SpriteComparator + { + bool operator()( const ::rtl::Reference<CanvasCustomSprite>& rLHS, + const ::rtl::Reference<CanvasCustomSprite>& rRHS ) const + { + const double nPrioL( rLHS->getPriority() ); + const double nPrioR( rRHS->getPriority() ); + + // if prios are equal, tie-break on ptr value + return nPrioL == nPrioR ? rLHS.get() < rRHS.get() : nPrioL < nPrioR; + } + }; + } + + ::sal_Bool SpriteDeviceHelper::showBuffer( bool bIsVisible, ::sal_Bool /*bUpdateAll*/ ) + { + // hidden or disposed? + if( !bIsVisible || !mpChildWindow || !mpSpriteCanvas ) + return false; + + if( !activateWindowContext() ) + return false; + + const ::Size& rOutputSize=mpChildWindow->GetSizePixel(); + initTransformation(rOutputSize); + + // render the actual spritecanvas content + mpSpriteCanvas->renderRecordedActions(); + + // render all sprites (in order of priority) on top of that + std::vector< ::rtl::Reference<CanvasCustomSprite> > aSprites; + std::copy(maActiveSprites.begin(), + maActiveSprites.end(), + std::back_insert_iterator< + std::vector< ::rtl::Reference< CanvasCustomSprite > > >(aSprites)); + std::sort(aSprites.begin(), + aSprites.end(), + SpriteComparator()); + std::for_each(aSprites.begin(), + aSprites.end(), + boost::mem_fn(&CanvasCustomSprite::renderSprite)); + + + // frame counter, other info + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslated(-1.0, 1.0, 0.0); + glScaled( 2.0 / rOutputSize.Width(), + -2.0 / rOutputSize.Height(), + 1.0 ); + + const double denominator( maLastUpdate.getElapsedTime() ); + maLastUpdate.reset(); + + const double fps(denominator == 0.0 ? 100.0 : 1.0/denominator); + std::vector<double> aVec; aVec.push_back(fps); + aVec.push_back(maActiveSprites.size()); + aVec.push_back(mpTextureCache->getCacheSize()); + aVec.push_back(mpTextureCache->getCacheMissCount()); + aVec.push_back(mpTextureCache->getCacheHitCount()); + renderOSD( aVec, 20 ); + + // switch buffer, sync etc. + const unx::Window aXWindow=mpChildWindow->GetSystemData()->aWindow; + unx::glXSwapBuffers(reinterpret_cast<unx::Display*>(mpDisplay), + aXWindow); + mpChildWindow->Show(); + unx::glXWaitGL(); + XSync( reinterpret_cast<unx::Display*>(mpDisplay), false ); + + // flush texture cache, such that it does not build up + // indefinitely. + // TODO: have max cache size/LRU time in config, prune only on + // demand + mpTextureCache->prune(); + + return true; + } + + ::sal_Bool SpriteDeviceHelper::switchBuffer( bool bIsVisible, ::sal_Bool bUpdateAll ) + { + // no difference for VCL canvas + return showBuffer( bIsVisible, bUpdateAll ); + } + + uno::Any SpriteDeviceHelper::isAccelerated() const + { + return ::com::sun::star::uno::makeAny(false); + } + + uno::Any SpriteDeviceHelper::getDeviceHandle() const + { + return uno::Any(); + } + + uno::Any SpriteDeviceHelper::getSurfaceHandle() const + { + return uno::Any(); + } + + uno::Reference<rendering::XColorSpace> SpriteDeviceHelper::getColorSpace() const + { + // always the same + return uno::Reference<rendering::XColorSpace>( + ::canvas::tools::getStdColorSpace(), + uno::UNO_QUERY); + } + + void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle& rBounds ) + { + if( mpChildWindow ) + mpChildWindow->setPosSizePixel( + 0,0,rBounds.Width,rBounds.Height); + } + + void SpriteDeviceHelper::dumpScreenContent() const + { + SAL_INFO("canvas.ogl", BOOST_CURRENT_FUNCTION ); + } + + void SpriteDeviceHelper::show( const ::rtl::Reference< CanvasCustomSprite >& xSprite ) + { + maActiveSprites.insert(xSprite); + } + + void SpriteDeviceHelper::hide( const ::rtl::Reference< CanvasCustomSprite >& xSprite ) + { + maActiveSprites.erase(xSprite); + } + + static void setupUniforms( unsigned int nProgramId, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + const GLint nTransformLocation = glGetUniformLocation(nProgramId, + "m_transform" ); + // OGL is column-major + float aTexTransform[] = + { + float(rTexTransform.get(0,0)), float(rTexTransform.get(1,0)), + float(rTexTransform.get(0,1)), float(rTexTransform.get(1,1)), + float(rTexTransform.get(0,2)), float(rTexTransform.get(1,2)) + }; + glUniformMatrix3x2fv(nTransformLocation,1,false,aTexTransform); + } + + static void setupUniforms( unsigned int nProgramId, + const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + glUseProgram(nProgramId); + + GLuint nColorsTexture; + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &nColorsTexture); + glBindTexture(GL_TEXTURE_1D, nColorsTexture); + + const sal_Int32 nColors=rStops.getLength(); + glTexImage1D( GL_TEXTURE_1D, 0, GL_RGBA, nColors, 0, GL_RGBA, GL_DOUBLE, pColors ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + + GLuint nStopsTexture; + glActiveTexture(GL_TEXTURE1); + glGenTextures(1, &nStopsTexture); + glBindTexture(GL_TEXTURE_1D, nStopsTexture); + + glTexImage1D( GL_TEXTURE_1D, 0, GL_ALPHA, nColors, 0, GL_ALPHA, GL_DOUBLE, rStops.getConstArray() ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + + const GLint nColorArrayLocation = glGetUniformLocation(nProgramId, + "t_colorArray4d" ); + glUniform1i( nColorArrayLocation, 0 ); // unit 0 + + const GLint nStopArrayLocation = glGetUniformLocation(nProgramId, + "t_stopArray1d" ); + glUniform1i( nStopArrayLocation, 1 ); // unit 1 + + const GLint nNumColorLocation = glGetUniformLocation(nProgramId, + "i_nColors" ); + glUniform1i( nNumColorLocation, nColors-1 ); + + setupUniforms(nProgramId,rTexTransform); + } + + static void setupUniforms( unsigned int nProgramId, + const rendering::ARGBColor& rStartColor, + const rendering::ARGBColor& rEndColor, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + glUseProgram(nProgramId); + + const GLint nStartColorLocation = glGetUniformLocation(nProgramId, + "v_startColor4d" ); + glUniform4f(nStartColorLocation, + rStartColor.Red, + rStartColor.Green, + rStartColor.Blue, + rStartColor.Alpha); + + const GLint nEndColorLocation = glGetUniformLocation(nProgramId, + "v_endColor4d" ); + glUniform4f(nEndColorLocation, + rEndColor.Red, + rEndColor.Green, + rEndColor.Blue, + rEndColor.Alpha); + + setupUniforms(nProgramId,rTexTransform); + } + + void SpriteDeviceHelper::useLinearGradientShader( const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + if( rStops.getLength() > 2 ) + setupUniforms(mnLinearMultiColorGradientProgram, pColors, rStops, rTexTransform); + else + setupUniforms(mnLinearTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform); + } + + void SpriteDeviceHelper::useRadialGradientShader( const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + if( rStops.getLength() > 2 ) + setupUniforms(mnRadialMultiColorGradientProgram, pColors, rStops, rTexTransform); + else + setupUniforms(mnRadialTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform); + } + + void SpriteDeviceHelper::useRectangularGradientShader( const rendering::ARGBColor* pColors, + const uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ) + { + if( rStops.getLength() > 2 ) + setupUniforms(mnRectangularMultiColorGradientProgram, pColors, rStops, rTexTransform); + else + setupUniforms(mnRectangularTwoColorGradientProgram, pColors[0], pColors[1], rTexTransform); + } + + bool SpriteDeviceHelper::activatePBufferContext(const ::basegfx::B2IVector& rSize, + unsigned int PBuffer) const + { + if( !glXMakeCurrent( reinterpret_cast<unx::Display*>(mpDisplay), + PBuffer, + reinterpret_cast<unx::GLXContext>(mpGLPBufContext)) ) + { + SAL_INFO("canvas.ogl", "SpriteDeviceHelper::activatePBufferContext(): cannot activate GL context"); + return false; + } + + initContext(); + initTransformation( + ::Size( + rSize.getX(), + rSize.getY()), + true); + + return true; + } + + bool SpriteDeviceHelper::activateWindowContext() const + { + const unx::Window aXWindow=mpChildWindow->GetSystemData()->aWindow; + if( !glXMakeCurrent( reinterpret_cast<unx::Display*>(mpDisplay), + aXWindow, + reinterpret_cast<unx::GLXContext>(mpGLContext)) ) + { + SAL_INFO("canvas.ogl", "SpriteDeviceHelper::activateWindowContext(): cannot activate GL context"); + return false; + } + + return true; + } + + bool SpriteDeviceHelper::updatePBufferTexture( const ::basegfx::B2IVector& rSize, + unsigned int nTextId ) const + { + glBindTexture( GL_TEXTURE_2D, nTextId ); + glEnable(GL_TEXTURE_2D); + glCopyTexSubImage2D( GL_TEXTURE_2D, + 0, 0, 0, 0, 0, + rSize.getX(), + rSize.getY() ); + glBindTexture(GL_TEXTURE_2D, 0); + + return true; + } + + namespace + { + class BufferContextImpl : public IBufferContext + { + ::basegfx::B2IVector maSize; + const SpriteDeviceHelper& mrDeviceHelper; + unx::GLXPbuffer mpPBuffer; + unx::Display* mpDisplay; + unsigned int mnTexture; + + virtual bool startBufferRendering() + { + return mrDeviceHelper.activatePBufferContext(maSize,mpPBuffer); + } + + virtual bool endBufferRendering() + { + mrDeviceHelper.updatePBufferTexture(maSize,mnTexture); + if( !mrDeviceHelper.activateWindowContext() ) + return false; + + glBindTexture( GL_TEXTURE_2D, mnTexture ); + + return true; + } + + public: + BufferContextImpl(const SpriteDeviceHelper& rDeviceHelper, + unx::GLXPbuffer pBuffer, + unx::Display* pDisplay, + const ::basegfx::B2IVector& rSize) : + maSize(rSize), + mrDeviceHelper(rDeviceHelper), + mpPBuffer(pBuffer), + mpDisplay(pDisplay), + mnTexture(0) + { + glGenTextures( 1, &mnTexture ); +#if 1 + glBindTexture( GL_TEXTURE_2D, mnTexture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, + maSize.getX(), maSize.getY(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, new int[maSize.getX()*maSize.getY()] ); +#endif + } + + ~BufferContextImpl() + { +#if 0 + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures( 1, &mnTexture ); + glXDestroyPbuffer( mpDisplay, mpPBuffer ); +#endif + } + }; + } + + IBufferContextSharedPtr SpriteDeviceHelper::createBufferContext(const ::basegfx::B2IVector& rSize) const + { + int pBufAttribs[] = + { + GLX_PBUFFER_WIDTH, rSize.getX(), + GLX_PBUFFER_HEIGHT, rSize.getY(), + GLX_LARGEST_PBUFFER, False, + None + }; + + unx::GLXPbuffer pBuffer; + pBuffer = unx::glXCreatePbuffer( reinterpret_cast<unx::Display*>(mpDisplay), + *reinterpret_cast<unx::GLXFBConfig*>(mpFBConfig), + pBufAttribs ); + + IBufferContextSharedPtr pRet; + if( pBuffer ) + pRet.reset(new BufferContextImpl( + *this, + pBuffer, + reinterpret_cast<unx::Display*>(mpDisplay), + rSize)); + + return pRet; + } + + TextureCache& SpriteDeviceHelper::getTextureCache() const + { + return *mpTextureCache; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_spritedevicehelper.hxx b/canvas/source/opengl/ogl_spritedevicehelper.hxx new file mode 100644 index 000000000000..c87bdf84d18f --- /dev/null +++ b/canvas/source/opengl/ogl_spritedevicehelper.hxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_SPRITEDEVICEHELPER_HXX +#define OGL_SPRITEDEVICEHELPER_HXX + +#include <rtl/ref.hxx> +#include <canvas/elapsedtime.hxx> +#include <com/sun/star/rendering/XGraphicDevice.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +#include "ogl_buffercontext.hxx" + +#include <set> + + +class Window; +class SystemChildWindow; +namespace basegfx{ class B2IVector; class B2DHomMatrix; } +namespace com { namespace sun { namespace star { + namespace awt { struct Rectangle; } + namespace geometry { struct AffineMatrix2D; } +}}} + +namespace oglcanvas +{ + class TextureCache; + class SpriteCanvas; + class CanvasCustomSprite; + class CanvasHelper; + + class SpriteDeviceHelper : private ::boost::noncopyable + { + public: + SpriteDeviceHelper(); + + void init( Window& rWindow, + SpriteCanvas& rSpriteCanvas, + const ::com::sun::star::awt::Rectangle& rViewArea ); + + /// Dispose all internal references + void disposing(); + + // XWindowGraphicDevice + ::com::sun::star::geometry::RealSize2D getPhysicalResolution(); + ::com::sun::star::geometry::RealSize2D getPhysicalSize(); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XLinePolyPolygon2D > createCompatibleLinePolyPolygon( + const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealPoint2D > >& points ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBezierPolyPolygon2D > createCompatibleBezierPolyPolygon( + const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealBezierSegment2D > >& points ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmap > createCompatibleBitmap( + const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::geometry::IntegerSize2D& size ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XVolatileBitmap > createVolatileBitmap( + const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::geometry::IntegerSize2D& size ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmap > createCompatibleAlphaBitmap( + const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::geometry::IntegerSize2D& size ); + ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XVolatileBitmap > createVolatileAlphaBitmap( + const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XGraphicDevice >& rDevice, + const ::com::sun::star::geometry::IntegerSize2D& size ); + + sal_Bool hasFullScreenMode( ); + sal_Bool enterFullScreenMode( sal_Bool bEnter ); + + ::sal_Int32 createBuffers( ::sal_Int32 nBuffers ); + void destroyBuffers( ); + ::sal_Bool showBuffer( bool bIsVisible, ::sal_Bool bUpdateAll ); + ::sal_Bool switchBuffer( bool bIsVisible, ::sal_Bool bUpdateAll ); + + ::com::sun::star::uno::Any isAccelerated() const; + ::com::sun::star::uno::Any getDeviceHandle() const; + ::com::sun::star::uno::Any getSurfaceHandle() const; + ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XColorSpace > getColorSpace() const; + + void notifySizeUpdate( const ::com::sun::star::awt::Rectangle& rBounds ); + + /** called when DumpScreenContent property is enabled on + XGraphicDevice, and writes out bitmaps of current screen. + */ + void dumpScreenContent() const; + + void show( const ::rtl::Reference< CanvasCustomSprite >& ); + void hide( const ::rtl::Reference< CanvasCustomSprite >& ); + + /// enable linear gradient shader "texture" with given parameters + void useLinearGradientShader( const ::com::sun::star::rendering::ARGBColor* pColors, + const ::com::sun::star::uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ); + /// enable radial gradient shader "texture" with given parameters + void useRadialGradientShader( const ::com::sun::star::rendering::ARGBColor* pColors, + const ::com::sun::star::uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ); + /// enable rectangular gradient shader "texture" with given parameters + void useRectangularGradientShader( const ::com::sun::star::rendering::ARGBColor* pColors, + const ::com::sun::star::uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ); + + /// create a pbuffer context (for rendering into background surface) + IBufferContextSharedPtr createBufferContext(const ::basegfx::B2IVector& rSize) const; + + /// Get instance of internal texture cache + TextureCache& getTextureCache() const; + + //////////////////////////////////////////////////////// + + // nobody except IBufferContext implementations are supposed + // to use this + bool activatePBufferContext(const ::basegfx::B2IVector& rSize, + unsigned int PBuffer) const; + bool activateWindowContext() const; + bool updatePBufferTexture( const ::basegfx::B2IVector&, + unsigned int ) const; + + private: + void resize( const ::basegfx::B2IVector& rNewSize ); + + void compileShader(unsigned int& o_rShaderHandle, + unsigned int eShaderType, + const char* pShaderSourceCode); + void linkShaders(unsigned int& o_rProgramHandle, + unsigned int nVertexProgramId, + unsigned int nFragmentProgramId); + + /** Phyical output device + + Deliberately not a refcounted reference, because of + potential circular references for canvas. Needed to + create bitmaps + */ + com::sun::star::rendering::XGraphicDevice* mpDevice; + + /// Pointer to sprite canvas (owner of this helper), needed to create bitmaps + SpriteCanvas* mpSpriteCanvas; + + std::set< ::rtl::Reference< CanvasCustomSprite > > maActiveSprites; + + /// For the frame counter timings + ::canvas::tools::ElapsedTime maLastUpdate; + + boost::shared_ptr<SystemChildWindow> mpChildWindow; + void* mpDisplay; + void* mpGLContext; + void* mpGLPBufContext; + void* mpFBConfig; + + boost::shared_ptr<TextureCache> mpTextureCache; + + unsigned int mnDummyVertexProgram; + + unsigned int mnLinearTwoColorGradientFragmentProgram; + unsigned int mnLinearMultiColorGradientFragmentProgram; + unsigned int mnRadialTwoColorGradientFragmentProgram; + unsigned int mnRadialMultiColorGradientFragmentProgram; + unsigned int mnRectangularTwoColorGradientFragmentProgram; + unsigned int mnRectangularMultiColorGradientFragmentProgram; + unsigned int mnLinearTwoColorGradientProgram; + unsigned int mnLinearMultiColorGradientProgram; + unsigned int mnRadialTwoColorGradientProgram; + unsigned int mnRadialMultiColorGradientProgram; + unsigned int mnRectangularTwoColorGradientProgram; + unsigned int mnRectangularMultiColorGradientProgram; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_textlayout.cxx b/canvas/source/opengl/ogl_textlayout.cxx new file mode 100644 index 000000000000..42f8d97221ea --- /dev/null +++ b/canvas/source/opengl/ogl_textlayout.cxx @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ogl_textlayout.hxx" + +#include <canvas/debug.hxx> +#include <canvas/verbosetrace.hxx> +#include <tools/diagnose_ex.h> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/numeric/ftools.hxx> + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + TextLayout::TextLayout( const rendering::StringContext& aText, + sal_Int8 nDirection, + sal_Int64 /*nRandomSeed*/, + const CanvasFont::ImplRef& rFont ) : + TextLayoutBaseT( m_aMutex ), + maText( aText ), + maLogicalAdvancements(), + mpFont( rFont ), + mnTextDirection( nDirection ) + { + } + + void SAL_CALL TextLayout::disposing() + { + mpFont.reset(); + } + + // XTextLayout + uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > >(); + } + + uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return uno::Sequence< geometry::RealRectangle2D >(); + } + + uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return uno::Sequence< geometry::RealRectangle2D >(); + } + + uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return maLogicalAdvancements; + } + + void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if( aAdvancements.getLength() != maText.Length ) + { + SAL_INFO("canvas.ogl", "TextLayout::applyLogicalAdvancements(): mismatching number of advancements"); + throw lang::IllegalArgumentException(); + } + + maLogicalAdvancements = aAdvancements; + } + + geometry::RealRectangle2D SAL_CALL TextLayout::queryTextBounds( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_THROW( mpFont.get(), + "TextLayout::queryTextBounds(): invalid font" ); + + // fake text bounds by either taking the advancement values, + // or assuming square glyph boxes (width similar to height) + const rendering::FontRequest& rFontRequest( mpFont->getFontRequest() ); + const double nFontSize( ::std::max( rFontRequest.CellSize, + rFontRequest.ReferenceAdvancement ) ); + if( maLogicalAdvancements.getLength() ) + { + return geometry::RealRectangle2D( 0, -nFontSize/2, + maLogicalAdvancements[ maLogicalAdvancements.getLength()-1 ], + nFontSize/2 ); + } + else + { + return geometry::RealRectangle2D( 0, -nFontSize/2, + nFontSize * maText.Length, + nFontSize/2 ); + } + } + + double SAL_CALL TextLayout::justify( double /*nSize*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return 0.0; + } + + double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& /*aNextLayouts*/, + double /*nSize*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return 0.0; + } + + rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& /*aHitPoint*/ ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return rendering::TextHit(); + } + + rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 /*nInsertionIndex*/, + sal_Bool /*bExcludeLigatures*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return rendering::Caret(); + } + + sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 /*nStartIndex*/, + sal_Int32 /*nCaretAdvancement*/, + sal_Bool /*bExcludeLigatures*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return 0; + } + + uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 /*nStartIndex*/, + sal_Int32 /*nEndIndex*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return uno::Reference< rendering::XPolyPolygon2D >(); + } + + uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 /*nStartIndex*/, + sal_Int32 /*nEndIndex*/ ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return uno::Reference< rendering::XPolyPolygon2D >(); + } + + double SAL_CALL TextLayout::getBaselineOffset( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + return 0.0; + } + + sal_Int8 SAL_CALL TextLayout::getMainTextDirection( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return mnTextDirection; + } + + uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return mpFont.getRef(); + } + + rendering::StringContext SAL_CALL TextLayout::getText( ) throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return maText; + } + + bool TextLayout::draw( const rendering::ViewState& /*rViewState*/, + const rendering::RenderState& /*rRenderState*/, + const uno::Reference< rendering::XGraphicDevice >& /*xGraphicDevice*/ ) const + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO + + return true; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_textlayout.hxx b/canvas/source/opengl/ogl_textlayout.hxx new file mode 100644 index 000000000000..64c50f7f2bb8 --- /dev/null +++ b/canvas/source/opengl/ogl_textlayout.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_TEXTLAYOUT_HXX +#define OGL_TEXTLAYOUT_HXX + +#include <cppuhelper/compbase1.hxx> +#include <comphelper/broadcasthelper.hxx> + +#include <com/sun/star/rendering/XTextLayout.hpp> + +#include <basegfx/vector/b2isize.hxx> + +#include <boost/utility.hpp> + +#include "ogl_canvasfont.hxx" + + +/* Definition of TextLayout class */ + +namespace oglcanvas +{ + typedef ::cppu::WeakComponentImplHelper1< ::com::sun::star::rendering::XTextLayout > TextLayoutBaseT; + + class TextLayout : public ::comphelper::OBaseMutex, + public TextLayoutBaseT, + private ::boost::noncopyable + { + public: + TextLayout( const ::com::sun::star::rendering::StringContext& aText, + sal_Int8 nDirection, + sal_Int64 nRandomSeed, + const CanvasFont::ImplRef& rFont ); + + /// Dispose all internal references + virtual void SAL_CALL disposing(); + + // XTextLayout + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D > > SAL_CALL queryTextShapes( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealRectangle2D > SAL_CALL queryInkMeasures( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::geometry::RealRectangle2D > SAL_CALL queryMeasures( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Sequence< double > SAL_CALL queryLogicalAdvancements( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL applyLogicalAdvancements( const ::com::sun::star::uno::Sequence< double >& aAdvancements ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::geometry::RealRectangle2D SAL_CALL queryTextBounds( ) throw (::com::sun::star::uno::RuntimeException); + virtual double SAL_CALL justify( double nSize ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual double SAL_CALL combinedJustify( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XTextLayout > >& aNextLayouts, double nSize ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::rendering::TextHit SAL_CALL getTextHit( const ::com::sun::star::geometry::RealPoint2D& aHitPoint ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::rendering::Caret SAL_CALL getCaret( sal_Int32 nInsertionIndex, sal_Bool bExcludeLigatures ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + virtual sal_Int32 SAL_CALL getNextInsertionIndex( sal_Int32 nStartIndex, sal_Int32 nCaretAdvancement, sal_Bool bExcludeLigatures ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D > SAL_CALL queryVisualHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XPolyPolygon2D > SAL_CALL queryLogicalHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException); + virtual double SAL_CALL getBaselineOffset( ) throw (::com::sun::star::uno::RuntimeException); + virtual sal_Int8 SAL_CALL getMainTextDirection( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XCanvasFont > SAL_CALL getFont( ) throw (::com::sun::star::uno::RuntimeException); + virtual ::com::sun::star::rendering::StringContext SAL_CALL getText( ) throw (::com::sun::star::uno::RuntimeException); + + bool draw( const ::com::sun::star::rendering::ViewState& rViewState, + const ::com::sun::star::rendering::RenderState& rRenderState, + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XGraphicDevice >& xGraphicDevice ) const; + + private: + ::com::sun::star::rendering::StringContext maText; + ::com::sun::star::uno::Sequence< double > maLogicalAdvancements; + CanvasFont::ImplRef mpFont; + sal_Int8 mnTextDirection; + }; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_texturecache.cxx b/canvas/source/opengl/ogl_texturecache.cxx new file mode 100644 index 000000000000..952f36a231ce --- /dev/null +++ b/canvas/source/opengl/ogl_texturecache.cxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#define GL_GLEXT_PROTOTYPES + +#include "ogl_texturecache.hxx" + +#include <com/sun/star/geometry/IntegerSize2D.hpp> + +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glext.h> + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + TextureCache::TextureCache() : + maCache(101), + mnMissCount(0), + mnHitCount(0) + {} + + TextureCache::~TextureCache() + { + flush(); + } + + void TextureCache::flush() + { + // un-bind any texture + glBindTexture(GL_TEXTURE_2D, 0); + + // delete all cached textures + TextureCacheMapT::const_iterator aCurr=maCache.begin(); + const TextureCacheMapT::const_iterator aEnd=maCache.end(); + while( aCurr != aEnd ) + { + glDeleteTextures(1, &aCurr->second.nTexture); + ++aCurr; + } + + maCache.clear(); + mnMissCount = 0; + mnHitCount = 0; + } + + void TextureCache::prune() + { + // un-bind any texture + glBindTexture(GL_TEXTURE_2D, 0); + + // delete already "old" textures, mark "new" entries "old" + TextureCacheMapT::iterator aNext; + TextureCacheMapT::iterator aCurr=maCache.begin(); + const TextureCacheMapT::iterator aEnd=maCache.end(); + while( aCurr != aEnd ) + { + aNext=aCurr; ++aNext; + if( aCurr->second.bOld ) + { + glDeleteTextures(1, &aCurr->second.nTexture); + maCache.erase(aCurr); + } + else + { + aCurr->second.bOld = true; + } + aCurr=aNext; + } + + mnMissCount = 0; + mnHitCount = 0; + } + + unsigned int TextureCache::getTexture( const geometry::IntegerSize2D& rPixelSize, + const sal_Int8* pPixel, + sal_uInt32 nPixelCrc32) const + { + unsigned int nTexture(0); + + // texture already cached? + TextureCacheMapT::iterator aCacheEntry; + if( (aCacheEntry=maCache.find(nPixelCrc32)) == maCache.end() ) + { + // nope, insert new entry + glGenTextures(1, &nTexture); + glBindTexture(GL_TEXTURE_2D, nTexture); + + // TODO(E3): handle limited texture sizes - + // glGetIntegerv(GL_MAX_TEXTURE_SIZE) + glTexImage2D(GL_TEXTURE_2D, + 0, + 4, + rPixelSize.Width, + rPixelSize.Height, + 0, + GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8_REV, + pPixel); + + maCache[nPixelCrc32].nTexture = nTexture; + ++mnMissCount; + + return nTexture; + } + else + { + nTexture = aCacheEntry->second.nTexture; + aCacheEntry->second.bOld = false; + ++mnHitCount; + } + + return nTexture; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_texturecache.hxx b/canvas/source/opengl/ogl_texturecache.hxx new file mode 100644 index 000000000000..0f81a2603ae2 --- /dev/null +++ b/canvas/source/opengl/ogl_texturecache.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_TEXTURECACHE_HXX +#define OGL_TEXTURECACHE_HXX + +#include <sal/types.h> +#include <unordered_map> + +namespace com { namespace sun { namespace star { + namespace geometry { struct IntegerSize2D; } +}}} + +namespace oglcanvas +{ + class TextureCache + { + public: + TextureCache(); + ~TextureCache(); + + /// clear whole cache, reset statistic counters + void flush(); + + /** prune old entries from cache + + Everytime this method is called, all cache entries are set + to "old". If subsequently not used by getTexture(), + they'll be entitled for expunge on the next prune() + call. Resets statistic counters. + */ + void prune(); + + /// Statistics + size_t getCacheSize() const { return maCache.size(); }; + sal_uInt32 getCacheMissCount() const { return mnMissCount; } + sal_uInt32 getCacheHitCount() const { return mnHitCount; } + + unsigned int getTexture( const ::com::sun::star::geometry::IntegerSize2D& rPixelSize, + const sal_Int8* pPixel, + sal_uInt32 nPixelCrc32) const; + private: + struct CacheEntry + { + CacheEntry() : nTexture(0), bOld(false) {} + unsigned int nTexture; + bool bOld; + }; + typedef std::unordered_map<sal_uInt32,CacheEntry> TextureCacheMapT; + mutable TextureCacheMapT maCache; + mutable sal_uInt32 mnMissCount; + mutable sal_uInt32 mnHitCount; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/ogl_tools.hxx b/canvas/source/opengl/ogl_tools.hxx new file mode 100644 index 000000000000..d61ab45dd447 --- /dev/null +++ b/canvas/source/opengl/ogl_tools.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef OGL_CANVAS_TOOLS_HXX +#define OGL_CANVAS_TOOLS_HXX + +#include <sal/config.h> +#include <GL/gl.h> + + +namespace oglcanvas +{ + struct TransformationPreserver + { + TransformationPreserver() + { glPushMatrix(); } + + ~TransformationPreserver() + { glPopMatrix(); } + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/opengl/oglcanvas.component b/canvas/source/opengl/oglcanvas.component new file mode 100644 index 000000000000..bd2d1039cb78 --- /dev/null +++ b/canvas/source/opengl/oglcanvas.component @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * +--> + +<component loader="com.sun.star.loader.SharedLibrary" prefix="oglcanvas" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.rendering.SpriteCanvas.OGL"> + <service name="com.sun.star.rendering.SpriteCanvas.OGL"/> + </implementation> +</component> |