diff options
Diffstat (limited to 'canvas/source/opengl')
23 files changed, 3564 insertions, 0 deletions
diff --git a/canvas/source/opengl/ogl_bitmapcanvashelper.cxx b/canvas/source/opengl/ogl_bitmapcanvashelper.cxx new file mode 100644 index 000000000..0b95dfb2a --- /dev/null +++ b/canvas/source/opengl/ogl_bitmapcanvashelper.cxx @@ -0,0 +1,65 @@ +/* -*- 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 <sal/config.h> + +#include <canvas/canvastools.hxx> + +#include "ogl_bitmapcanvashelper.hxx" + + +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); + } + + uno::Reference< rendering::XBitmap > BitmapCanvasHelper::getScaledBitmap( const geometry::RealSize2D& /*newSize*/, + 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 >(); + } + + 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() const + { + return ::canvas::tools::getStdMemoryLayout(getSize()); + } + +} + +/* 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 000000000..5bdb9713d --- /dev/null +++ b/canvas/source/opengl/ogl_bitmapcanvashelper.hxx @@ -0,0 +1,71 @@ +/* -*- 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/. + */ + +#pragma once + +#include <com/sun/star/geometry/IntegerPoint2D.hpp> +#include <com/sun/star/geometry/IntegerRectangle2D.hpp> +#include <com/sun/star/rendering/IntegerBitmapLayout.hpp> + +#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( css::rendering::XGraphicDevice& rDevice, + SpriteDeviceHelper& rDeviceHelper, + const css::geometry::IntegerSize2D& rSize ); + + // BitmapCanvasHelper functionality + // ================================ + + const css::geometry::IntegerSize2D& getSize() const { return maSize; } + + css::uno::Reference< css::rendering::XBitmap > + getScaledBitmap( const css::geometry::RealSize2D& newSize, + bool beFast ); + + css::uno::Sequence< sal_Int8 > + getData( css::rendering::IntegerBitmapLayout& bitmapLayout, + const css::geometry::IntegerRectangle2D& rect ); + + css::uno::Sequence< sal_Int8 > + getPixel( css::rendering::IntegerBitmapLayout& bitmapLayout, + const css::geometry::IntegerPoint2D& pos ); + + css::rendering::IntegerBitmapLayout getMemoryLayout() const; + + private: + css::geometry::IntegerSize2D maSize; + }; +} + +/* 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 000000000..d4262d356 --- /dev/null +++ b/canvas/source/opengl/ogl_buffercontext.hxx @@ -0,0 +1,35 @@ +/* -*- 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/. + */ + +#pragma once + +#include <epoxy/gl.h> + +#include <sal/config.h> +#include <memory> + +namespace oglcanvas +{ + struct IBufferContext + { + virtual ~IBufferContext() {} + + /// start render to buffer. changes current framebuffer + virtual void startBufferRendering() = 0; + + /// end render to buffer. switches to default framebuffer + virtual void endBufferRendering() = 0; + + virtual GLuint getTextureId() = 0; + }; + + typedef std::shared_ptr<IBufferContext> IBufferContextSharedPtr; +} + +/* 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 000000000..fa5e53823 --- /dev/null +++ b/canvas/source/opengl/ogl_canvasbitmap.cxx @@ -0,0 +1,54 @@ +/* -*- 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 <sal/config.h> + +#include <tools/diagnose_ex.h> + +#include <utility> + +#include "ogl_canvasbitmap.hxx" + + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + CanvasBitmap::CanvasBitmap( const geometry::IntegerSize2D& rSize, + SpriteCanvasRef rDevice, + SpriteDeviceHelper& rDeviceHelper ) : + mpDevice(std::move( rDevice )) + { + ENSURE_OR_THROW( mpDevice.is(), + "CanvasBitmap::CanvasBitmap(): Invalid surface or device" ); + + maCanvasHelper.init( *mpDevice, rDeviceHelper, rSize ); + } + + CanvasBitmap::CanvasBitmap( const CanvasBitmap& rSrc ) : + mpDevice( rSrc.mpDevice ) + { + maCanvasHelper = rSrc.maCanvasHelper; + } + + void 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 000000000..5cc41bb19 --- /dev/null +++ b/canvas/source/opengl/ogl_canvasbitmap.hxx @@ -0,0 +1,72 @@ +/* -*- 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/. + */ + +#pragma once + +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/rendering/XBitmapCanvas.hpp> +#include <com/sun/star/rendering/XIntegerBitmap.hpp> + +#include <base/integerbitmapbase.hxx> +#include <base/basemutexhelper.hxx> +#include <base/bitmapcanvasbase.hxx> + +#include "ogl_bitmapcanvashelper.hxx" +#include "ogl_spritecanvas.hxx" + + +/* Definition of CanvasBitmap class */ + +namespace oglcanvas +{ + typedef ::cppu::WeakComponentImplHelper< css::rendering::XBitmapCanvas, + css::rendering::XIntegerBitmap > CanvasBitmapBase_Base; + typedef ::canvas::IntegerBitmapBase< + canvas::BitmapCanvasBase2< + ::canvas::BaseMutexHelper< 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 css::geometry::IntegerSize2D& rSize, + SpriteCanvasRef rDevice, + SpriteDeviceHelper& rDeviceHelper ); + + /** Create verbatim copy (including all recorded actions) + */ + CanvasBitmap( const CanvasBitmap& rSrc ); + + /// Dispose all internal references + virtual void disposeThis() override; + + /** Write out recorded actions + */ + bool renderRecordedActions() const; + + private: + /** MUST hold here, too, since CanvasHelper only contains a + raw pointer (without refcounting) + */ + SpriteCanvasRef mpDevice; + }; +} + +/* 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 000000000..4be2b5db2 --- /dev/null +++ b/canvas/source/opengl/ogl_canvascustomsprite.cxx @@ -0,0 +1,257 @@ +/* -*- 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 <sal/config.h> + +#include <epoxy/gl.h> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> +#include <basegfx/utils/canvastools.hxx> +#include <canvas/canvastools.hxx> +#include <o3tl/safeint.hxx> +#include <verifyinput.hxx> +#include <tools/diagnose_ex.h> + +#include "ogl_canvascustomsprite.hxx" +#include "ogl_canvastools.hxx" +#include "ogl_tools.hxx" + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + CanvasCustomSprite::CanvasCustomSprite( const css::geometry::RealSize2D& rSpriteSize, + const SpriteCanvasRef& rRefDevice, + SpriteDeviceHelper& rDeviceHelper ) : + mpSpriteCanvas( rRefDevice ), + maSize(rSpriteSize), + mfAlpha(0.0), + mfPriority(0.0) + { + ENSURE_OR_THROW( rRefDevice, + "CanvasCustomSprite::CanvasCustomSprite(): Invalid sprite canvas" ); + + ::canvas::tools::setIdentityAffineMatrix2D(maTransformation); + maCanvasHelper.init( *rRefDevice, + rDeviceHelper ); + } + + void CanvasCustomSprite::disposeThis() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + mpSpriteCanvas.clear(); + + // forward to parent + CanvasCustomSpriteBaseT::disposeThis(); + } + + void SAL_CALL CanvasCustomSprite::setAlpha( double alpha ) + { + 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 ) + { + canvas::tools::verifyArgs(aNewPos, viewState, renderState, + __func__, + 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 ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + maTransformation = aTransformation; + } + + void SAL_CALL CanvasCustomSprite::clip( const uno::Reference< rendering::XPolyPolygon2D >& xClip ) + { + mxClip = xClip; + } + + void SAL_CALL CanvasCustomSprite::setPriority( double nPriority ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + mfPriority = nPriority; + } + + void SAL_CALL CanvasCustomSprite::show() + { + ::osl::MutexGuard aGuard( m_aMutex ); + if( mpSpriteCanvas.is() ) + mpSpriteCanvas->show(this); + } + + void SAL_CALL CanvasCustomSprite::hide() + { + ::osl::MutexGuard aGuard( m_aMutex ); + if( mpSpriteCanvas.is() ) + mpSpriteCanvas->hide(this); + } + + uno::Reference< rendering::XCanvas > SAL_CALL CanvasCustomSprite::getContentCanvas() + { + 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() ) + { + // drafts. Need to render to temp surface before, and then + // composite that to screen + + // TODO(P3): buffer 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(); + GLuint nTexture = pBufferContext->getTextureId(); + glBindTexture(GL_TEXTURE_2D, nTexture); + + 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::triangulator::B2DTriangleVector rTriangulatedPolygon( + ::basegfx::triangulator::triangulate( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(mxClip))); + + glBegin(GL_TRIANGLES); + for( size_t i=0; i<rTriangulatedPolygon.size(); i++ ) + { + const::basegfx::triangulator::B2DTriangle& rCandidate(rTriangulatedPolygon[i]); + glTexCoord2f( + rCandidate.getA().getX()/fWidth, + rCandidate.getA().getY()/fHeight); + glVertex2d( + rCandidate.getA().getX(), + rCandidate.getA().getY()); + + glTexCoord2f( + rCandidate.getB().getX()/fWidth, + rCandidate.getB().getY()/fHeight); + glVertex2d( + rCandidate.getB().getX(), + rCandidate.getB().getY()); + + glTexCoord2f( + rCandidate.getC().getX()/fWidth, + rCandidate.getC().getY()/fHeight); + glVertex2d( + rCandidate.getC().getX(), + rCandidate.getC().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 { mfAlpha, mfPriority, o3tl::narrowing<double>(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 000000000..3cd43cf4f --- /dev/null +++ b/canvas/source/opengl/ogl_canvascustomsprite.hxx @@ -0,0 +1,92 @@ +/* -*- 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/. + */ + +#pragma once + +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/rendering/XCustomSprite.hpp> +#include <com/sun/star/rendering/XPolyPolygon2D.hpp> + +#include <basegfx/point/b2dpoint.hxx> + +#include <base/basemutexhelper.hxx> + +#include "ogl_spritecanvas.hxx" +#include "ogl_canvashelper.hxx" + + +namespace oglcanvas +{ + typedef ::cppu::WeakComponentImplHelper< css::rendering::XCustomSprite, + css::rendering::XCanvas > CanvasCustomSpriteBase_Base; + typedef ::canvas::CanvasBase< + ::canvas::BaseMutexHelper< 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 css::geometry::RealSize2D& rSpriteSize, + const SpriteCanvasRef& rRefDevice, + SpriteDeviceHelper& rDeviceHelper ); + + virtual void disposeThis() override; + + // XSprite + virtual void SAL_CALL setAlpha( double alpha ) override; + virtual void SAL_CALL move( const css::geometry::RealPoint2D& aNewPos, const css::rendering::ViewState& viewState, const css::rendering::RenderState& renderState ) override; + virtual void SAL_CALL transform( const css::geometry::AffineMatrix2D& aTransformation ) override; + virtual void SAL_CALL clip( const css::uno::Reference< css::rendering::XPolyPolygon2D >& aClip ) override; + virtual void SAL_CALL setPriority( double nPriority ) override; + virtual void SAL_CALL show() override; + virtual void SAL_CALL hide() override; + + // XCustomSprite + virtual css::uno::Reference< css::rendering::XCanvas > SAL_CALL getContentCanvas() override; + + 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 css::geometry::RealSize2D maSize; + + css::uno::Reference< css::rendering::XPolyPolygon2D > mxClip; + css::geometry::AffineMatrix2D maTransformation; + ::basegfx::B2DPoint maPosition; + double mfAlpha; + double mfPriority; + }; +} + +/* 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 000000000..7309b9acd --- /dev/null +++ b/canvas/source/opengl/ogl_canvasfont.cxx @@ -0,0 +1,70 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/rendering/FontMetrics.hpp> +#include <canvas/canvastools.hxx> +#include <utility> + +#include "ogl_canvasfont.hxx" +#include "ogl_textlayout.hxx" + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + CanvasFont::CanvasFont( rendering::FontRequest aFontRequest, + const uno::Sequence< beans::PropertyValue >& extraFontProperties, + const geometry::Matrix2D& fontMatrix ) : + CanvasFontBaseT( m_aMutex ), + maFontRequest(std::move( aFontRequest )), + mnEmphasisMark(0), + maFontMatrix( fontMatrix ) + { + ::canvas::tools::extractExtraFontProperties(extraFontProperties, mnEmphasisMark); + } + + uno::Reference< rendering::XTextLayout > SAL_CALL CanvasFont::createTextLayout( const rendering::StringContext& aText, + sal_Int8 nDirection, + sal_Int64 nRandomSeed ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return new TextLayout( aText, nDirection, nRandomSeed, ImplRef( this ) ); + } + + uno::Sequence< double > SAL_CALL CanvasFont::getAvailableSizes( ) + { + // TODO + return uno::Sequence< double >(); + } + + uno::Sequence< beans::PropertyValue > SAL_CALL CanvasFont::getExtraFontProperties( ) + { + // TODO + return uno::Sequence< beans::PropertyValue >(); + } + + rendering::FontRequest SAL_CALL CanvasFont::getFontRequest( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return maFontRequest; + } + + rendering::FontMetrics SAL_CALL CanvasFont::getFontMetrics( ) + { + // TODO + return rendering::FontMetrics(); + } + +} + +/* 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 000000000..13b0f78a9 --- /dev/null +++ b/canvas/source/opengl/ogl_canvasfont.hxx @@ -0,0 +1,61 @@ +/* -*- 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/. + */ + +#pragma once + +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> + +#include <com/sun/star/rendering/XCanvasFont.hpp> +#include <com/sun/star/geometry/Matrix2D.hpp> + +#include <rtl/ref.hxx> + + +/* Definition of CanvasFont class */ + +namespace oglcanvas +{ + class SpriteCanvas; + + typedef ::cppu::WeakComponentImplHelper< css::rendering::XCanvasFont > CanvasFontBaseT; + + class CanvasFont : public ::cppu::BaseMutex, + public CanvasFontBaseT + { + public: + typedef rtl::Reference<CanvasFont> ImplRef; + + /// make noncopyable + CanvasFont(const CanvasFont&) = delete; + const CanvasFont& operator=(const CanvasFont&) = delete; + + CanvasFont( css::rendering::FontRequest fontRequest, + const css::uno::Sequence< css::beans::PropertyValue >& extraFontProperties, + const css::geometry::Matrix2D& fontMatrix ); + + // XCanvasFont + virtual css::uno::Reference< css::rendering::XTextLayout > SAL_CALL createTextLayout( const css::rendering::StringContext& aText, sal_Int8 nDirection, sal_Int64 nRandomSeed ) override; + virtual css::rendering::FontRequest SAL_CALL getFontRequest( ) override; + virtual css::rendering::FontMetrics SAL_CALL getFontMetrics( ) override; + virtual css::uno::Sequence< double > SAL_CALL getAvailableSizes( ) override; + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getExtraFontProperties( ) override; + + const css::geometry::Matrix2D& getFontMatrix() const { return maFontMatrix; } + + sal_uInt32 getEmphasisMark() const { return mnEmphasisMark; } + + private: + css::rendering::FontRequest maFontRequest; + sal_uInt32 mnEmphasisMark; + css::geometry::Matrix2D maFontMatrix; + }; +} + +/* 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 000000000..9835cea5b --- /dev/null +++ b/canvas/source/opengl/ogl_canvashelper.cxx @@ -0,0 +1,947 @@ +/* -*- 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 <sal/config.h> + +#include <memory> +#include <functional> +#include <epoxy/gl.h> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/utils/canvastools.hxx> +#include <com/sun/star/rendering/CompositeOperation.hpp> +#include <rtl/crc.h> +#include <rtl/math.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/font.hxx> +#include <vcl/metric.hxx> +#include <vcl/virdev.hxx> + +#include "ogl_canvasbitmap.hxx" +#include "ogl_canvasfont.hxx" +#include "ogl_canvastools.hxx" +#include "ogl_texturecache.hxx" +#include "ogl_tools.hxx" + +#include "ogl_canvashelper.hxx" + +using namespace ::com::sun::star; +using namespace std::placeholders; + +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; + + std::function< bool ( + const CanvasHelper&, + const ::basegfx::B2DHomMatrix&, + GLenum, + GLenum, + const rendering::ARGBColor&, + const ::basegfx::B2DPolyPolygonVector&)> maFunction; + }; + + namespace + { + 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); + + for( const auto& rPoly : rPolyPolygons ) + renderPolyPolygon( rPoly ); + + 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); + + for( const auto& rPoly : rPolyPolygons ) + { + glBegin( GL_TRIANGLES ); + renderComplexPolyPolygon( rPoly ); + 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; + for( const auto& rPoly : rPolyPolygons ) + aBounds.expand( ::basegfx::utils::getRange( rPoly ) ); + 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::GradientType::Linear: + rHelper.getDeviceHelper()->useLinearGradientShader(pColors, + rValues.maStops, + aTextureTransform); + break; + + case ::canvas::ParametricPolyPolygon::GradientType::Elliptical: + rHelper.getDeviceHelper()->useRadialGradientShader(pColors, + rValues.maStops, + aTextureTransform); + break; + + case ::canvas::ParametricPolyPolygon::GradientType::Rectangular: + rHelper.getDeviceHelper()->useRectangularGradientShader(pColors, + rValues.maStops, + aTextureTransform); + break; + + default: + ENSURE_OR_THROW( false, + "CanvasHelper lcl_fillGradientPolyPolygon(): Unexpected case" ); + } + + + for( const auto& rPoly : rPolyPolygons ) + { + glBegin(GL_TRIANGLES); + renderComplexPolyPolygon( rPoly ); + 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; + for( const auto& rPolyPolygon : rPolyPolygons ) + aBounds.expand( ::basegfx::utils::getRange( rPolyPolygon ) ); + 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); + + for( const auto& rPolyPolygon : rPolyPolygons ) + { + glBegin(GL_TRIANGLES); + renderComplexPolyPolygon( rPolyPolygon ); + glEnd(); + } + + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + return true; + } + } + + CanvasHelper::CanvasHelper() : + mpDevice( nullptr ), + mpDeviceHelper( nullptr ) + {} + + 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 = nullptr; + mpDeviceHelper = nullptr; + } + + void CanvasHelper::init( rendering::XGraphicDevice& rDevice, + SpriteDeviceHelper& rDeviceHelper ) + { + mpDevice = &rDevice; + mpDeviceHelper = &rDeviceHelper; + } + + void CanvasHelper::clear() + { + mpRecordedActions->clear(); + } + + 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 = std::bind(&lcl_drawLine, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_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 ) + return; + + mpRecordedActions->push_back( Action() ); + Action& rAct=mpRecordedActions->back(); + + setupGraphicsState( rAct, viewState, renderState ); + + // TODO(F2): subdivide&render whole curve + rAct.maFunction = std::bind(&lcl_drawLine, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_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 >(nullptr); + } + + 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 >(nullptr); + } + + 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 >(nullptr); + } + + 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 >(nullptr); + } + + 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 >(nullptr); + } + + 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 >(nullptr); + } + + 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 = std::bind(&lcl_fillGradientPolyPolygon, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, + rValues, + textures[0], + std::placeholders::_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 = std::bind(&lcl_fillTexturedPolyPolygon, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, + textures[0], + aSize, + aARGBBytes, + rtl_crc32(0, + aARGBBytes.getConstArray(), + aARGBBytes.getLength()), + std::placeholders::_6); + } + // TODO(F1): handle non-integer case + } + } + } + + // TODO(P1): Provide caching here. + return uno::Reference< rendering::XCachedPrimitive >(nullptr); + } + + 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 >(nullptr); + } + + 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 >(nullptr); + } + + 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 ) + { + ScopedVclPtrInstance< VirtualDevice > pVDev; + pVDev->EnableOutput(false); + + auto pLayoutFont = xLayoutetText->getFont(); + CanvasFont* pFont=dynamic_cast<CanvasFont*>(pLayoutFont.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(); + vcl::Font aFont( + rFontRequest.FontDescription.FamilyName, + rFontRequest.FontDescription.StyleName, + Size( 0, ::basegfx::fround(rFontRequest.CellSize))); + + aFont.SetAlignment( ALIGN_BASELINE ); + aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE ); + aFont.SetVertical( rFontRequest.FontDescription.IsVertical==util::TriState_YES ); + aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) ); + aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL ); + + if (pFont->getEmphasisMark()) + aFont.SetEmphasisMark(FontEmphasisMark(pFont->getEmphasisMark())); + + // adjust to stretched font + if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11)) + { + const Size aSize = pVDev->GetFontMetric( aFont ).GetFontSize(); + 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.SetAverageFontWidth( nNewWidth ); + } + + // set font + pVDev->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.hasElements() ) + { + // create the DXArray + const sal_Int32 nLen( aLogicalAdvancements.getLength() ); + std::vector<sal_Int32> aDXArray(nLen); + for( sal_Int32 i=0; i<nLen; ++i ) + aDXArray[i] = basegfx::fround( aLogicalAdvancements[i] ); + + // get the glyphs + pVDev->GetTextOutlines(rAct.maPolyPolys, + rTxt.Text, + 0, + rTxt.StartPosition, + rTxt.Length, + 0, + aDXArray); + } + else + { + // get the glyphs + pVDev->GetTextOutlines(rAct.maPolyPolys, + rTxt.Text, + 0, + rTxt.StartPosition, + rTxt.Length ); + } + + // own copy, for thread safety + for( auto& rPoly : rAct.maPolyPolys ) + rPoly.makeUnique(); + + rAct.maFunction = &lcl_fillPolyPolygon; + } + } + + // TODO + return uno::Reference< rendering::XCachedPrimitive >(nullptr); + } + + 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 = std::bind(&lcl_drawOwnBitmap, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_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 = std::bind(&lcl_drawGenericBitmap, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_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 >(nullptr); + } + + 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); + } + + + 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 + ::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: + 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; + } + + if (renderState.DeviceColor.hasElements()) + o_action.maARGBColor = + mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]; + } + + bool CanvasHelper::renderRecordedActions() const + { + for( const auto& rRecordedAction : *mpRecordedActions ) + { + if( !rRecordedAction.maFunction( *this, + rRecordedAction.maTransform, + rRecordedAction.meSrcBlendMode, + rRecordedAction.meDstBlendMode, + rRecordedAction.maARGBColor, + rRecordedAction.maPolyPolys ) ) + return false; + } + + 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 000000000..49513983d --- /dev/null +++ b/canvas/source/opengl/ogl_canvashelper.hxx @@ -0,0 +1,219 @@ +/* -*- 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/. + */ + +#pragma once + +#include <com/sun/star/rendering/XCanvas.hpp> + +#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( css::rendering::XGraphicDevice& rDevice, + SpriteDeviceHelper& rDeviceHelper ); + + // CanvasHelper functionality + // ========================== + + // XCanvas (only providing, not implementing the + // interface. Also note subtle method parameter differences) + void clear(); + void drawLine( const css::rendering::XCanvas* pCanvas, + const css::geometry::RealPoint2D& aStartPoint, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + void drawBezier( const css::rendering::XCanvas* pCanvas, + const css::geometry::RealBezierSegment2D& aBezierSegment, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + css::uno::Reference< css::rendering::XCachedPrimitive > + drawPolyPolygon( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + css::uno::Reference< css::rendering::XCachedPrimitive > + strokePolyPolygon( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState, + const css::rendering::StrokeAttributes& strokeAttributes ); + css::uno::Reference< css::rendering::XCachedPrimitive > + strokeTexturedPolyPolygon( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState, + const css::uno::Sequence< + css::rendering::Texture >& textures, + const css::rendering::StrokeAttributes& strokeAttributes ); + css::uno::Reference< css::rendering::XCachedPrimitive > + strokeTextureMappedPolyPolygon( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState, + const css::uno::Sequence< + css::rendering::Texture >& textures, + const css::uno::Reference< + css::geometry::XMapping2D >& xMapping, + const css::rendering::StrokeAttributes& strokeAttributes ); + css::uno::Reference< css::rendering::XPolyPolygon2D > + queryStrokeShapes( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState, + const css::rendering::StrokeAttributes& strokeAttributes ); + css::uno::Reference< css::rendering::XCachedPrimitive > + fillPolyPolygon( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + css::uno::Reference< css::rendering::XCachedPrimitive > + fillTexturedPolyPolygon( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState, + const css::uno::Sequence< + css::rendering::Texture >& textures ); + css::uno::Reference< css::rendering::XCachedPrimitive > + fillTextureMappedPolyPolygon( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState, + const css::uno::Sequence< + css::rendering::Texture >& textures, + const css::uno::Reference< + css::geometry::XMapping2D >& xMapping ); + + css::uno::Reference< css::rendering::XCanvasFont > + createFont( const css::rendering::XCanvas* pCanvas, + const css::rendering::FontRequest& fontRequest, + const css::uno::Sequence< + css::beans::PropertyValue >& extraFontProperties, + const css::geometry::Matrix2D& fontMatrix ); + + css::uno::Sequence< css::rendering::FontInfo > + queryAvailableFonts( const css::rendering::XCanvas* pCanvas, + const css::rendering::FontInfo& aFilter, + const css::uno::Sequence< + css::beans::PropertyValue >& aFontProperties ); + + css::uno::Reference< css::rendering::XCachedPrimitive > + drawText( const css::rendering::XCanvas* pCanvas, + const css::rendering::StringContext& text, + const css::uno::Reference< + css::rendering::XCanvasFont >& xFont, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState, + sal_Int8 textDirection ); + + css::uno::Reference< css::rendering::XCachedPrimitive > + drawTextLayout( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XTextLayout >& layoutetText, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + + css::uno::Reference< css::rendering::XCachedPrimitive > + drawBitmap( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XBitmap >& xBitmap, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + css::uno::Reference< css::rendering::XCachedPrimitive > + drawBitmapModulated( const css::rendering::XCanvas* pCanvas, + const css::uno::Reference< + css::rendering::XBitmap >& xBitmap, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + css::uno::Reference< css::rendering::XGraphicDevice > + getDevice() { return css::uno::Reference< css::rendering::XGraphicDevice >(mpDevice); } + + /** Write out recorded actions + */ + bool renderRecordedActions() const; + + /** Retrieve number of recorded actions + */ + size_t getRecordedActionCount() const; + + SpriteDeviceHelper* getDeviceHelper() const { return mpDeviceHelper; } + css::rendering::XGraphicDevice* getDevice() const { return mpDevice; } + + struct Action; + typedef o3tl::cow_wrapper< std::vector<Action>, + o3tl::ThreadSafeRefCountingPolicy > RecordVectorT; + + private: + CanvasHelper( const CanvasHelper& ) = delete; + + void setupGraphicsState( Action& o_action, + const css::rendering::ViewState& viewState, + const css::rendering::RenderState& renderState ); + + /** Phyical output device + + Deliberately not a refcounted reference, because of + potential circular references for spritecanvas. + */ + css::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; + }; +} + +/* 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 000000000..c5fbe0255 --- /dev/null +++ b/canvas/source/opengl/ogl_canvastools.cxx @@ -0,0 +1,148 @@ +/* -*- 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 <sal/config.h> + +#include <epoxy/gl.h> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/utils/tools.hxx> +#include <com/sun/star/rendering/ARGBColor.hpp> + +#include "ogl_canvastools.hxx" + +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::triangulator::B2DTriangleVector rTriangulatedPolygon( + ::basegfx::triangulator::triangulate(aPolyPoly)); + + for( size_t i=0; i<rTriangulatedPolygon.size(); i++ ) + { + const::basegfx::triangulator::B2DTriangle& rCandidate(rTriangulatedPolygon[i]); + glTexCoord2f( + rCandidate.getA().getX()/nWidth, + rCandidate.getA().getY()/nHeight); + glVertex2d( + rCandidate.getA().getX(), + rCandidate.getA().getY()); + + glTexCoord2f( + rCandidate.getB().getX()/nWidth, + rCandidate.getB().getY()/nHeight); + glVertex2d( + rCandidate.getB().getX(), + rCandidate.getB().getY()); + + glTexCoord2f( + rCandidate.getC().getX()/nWidth, + rCandidate.getC().getY()/nHeight); + glVertex2d( + rCandidate.getC().getX(), + rCandidate.getC().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 + int(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(double rNumber : rNumbers) + { + aTmp.identity(); + aTmp.translate(0,y); + y += 1.2*scale; + + basegfx::B2DPolyPolygon aPoly= + basegfx::utils::number2PolyPolygon(rNumber,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 000000000..8e7029fbe --- /dev/null +++ b/canvas/source/opengl/ogl_canvastools.hxx @@ -0,0 +1,37 @@ +/* -*- 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/. + */ + +#pragma once + +#include <sal/config.h> +#include <vector> + +#include <epoxy/gl.h> + +namespace com::sun::star::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, + GLenum eSrcBlend, + GLenum eDstBlend, + const com::sun::star::rendering::ARGBColor& rColor ); + + void renderOSD( const std::vector<double>& rNumbers, double scale ); +} + +/* 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 000000000..047627dbf --- /dev/null +++ b/canvas/source/opengl/ogl_spritecanvas.cxx @@ -0,0 +1,167 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <com/sun/star/lang/NoSupportException.hpp> +#include <osl/mutex.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/opengl/OpenGLHelper.hxx> +#include <vcl/window.hxx> + +#include "ogl_canvascustomsprite.hxx" +#include "ogl_spritecanvas.hxx" + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + SpriteCanvas::SpriteCanvas( const uno::Sequence< uno::Any >& aArguments, + const uno::Reference< uno::XComponentContext >& /*rxContext*/ ) : + maArguments(aArguments) + { + } + + void SpriteCanvas::initialize() + { + // Only call initialize when not in probe mode + if( !maArguments.hasElements() ) + return; + + SAL_INFO("canvas.ogl", "SpriteCanvas::initialize called" ); + + /* aArguments: + 0: ptr to creating instance (Window or VirtualDevice) + 1: current bounds of creating instance + 2: bool, denoting always on top state for Window (always false for VirtualDevice) + 3: XWindow for creating Window (or empty for VirtualDevice) + 4: SystemGraphicsData as a streamed Any + */ + ENSURE_ARG_OR_THROW( maArguments.getLength() >= 4 && + maArguments[3].getValueTypeClass() == uno::TypeClass_INTERFACE, + "OpenGL SpriteCanvas::initialize: wrong number of arguments, or wrong types" ); + + uno::Reference< awt::XWindow > xParentWindow; + maArguments[3] >>= xParentWindow; + VclPtr<vcl::Window> pParentWindow = VCLUnoHelper::GetWindow(xParentWindow); + if( !pParentWindow ) + throw lang::NoSupportException( + "Parent window not VCL window, or canvas out-of-process!", nullptr); + + awt::Rectangle aRect; + maArguments[1] >>= aRect; + + // setup helper + maDeviceHelper.init( *pParentWindow, + *this, + aRect ); + maCanvasHelper.init( *this, maDeviceHelper ); + maArguments.realloc(0); + } + + void SpriteCanvas::disposeThis() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // forward to parent + SpriteCanvasBaseT::disposeThis(); + } + + sal_Bool SAL_CALL SpriteCanvas::showBuffer( sal_Bool bUpdateAll ) + { + ::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 && SpriteCanvasBaseT::showBuffer( bUpdateAll ); + } + + sal_Bool SAL_CALL SpriteCanvas::switchBuffer( sal_Bool bUpdateAll ) + { + ::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 && SpriteCanvasBaseT::switchBuffer( bUpdateAll ); + } + + uno::Reference< rendering::XAnimatedSprite > SAL_CALL SpriteCanvas::createSpriteFromAnimation( + const uno::Reference< rendering::XAnimation >& /*animation*/ ) + { + return uno::Reference< rendering::XAnimatedSprite >(); + } + + uno::Reference< rendering::XAnimatedSprite > SAL_CALL SpriteCanvas::createSpriteFromBitmaps( + const uno::Sequence< uno::Reference< rendering::XBitmap > >& /*animationBitmaps*/, + ::sal_Int8 /*interpolationMode*/ ) + { + return uno::Reference< rendering::XAnimatedSprite >(); + } + + uno::Reference< rendering::XCustomSprite > SAL_CALL SpriteCanvas::createCustomSprite( + const geometry::RealSize2D& spriteSize ) + { + return uno::Reference< rendering::XCustomSprite >( + new CanvasCustomSprite(spriteSize, this, maDeviceHelper) ); + } + + uno::Reference< rendering::XSprite > SAL_CALL SpriteCanvas::createClonedSprite( + const uno::Reference< rendering::XSprite >& /*original*/ ) + { + return uno::Reference< rendering::XSprite >(); + } + + sal_Bool SAL_CALL SpriteCanvas::updateScreen(sal_Bool bUpdateAll) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return maDeviceHelper.showBuffer(mbIsVisible, bUpdateAll); + } + + OUString SAL_CALL SpriteCanvas::getServiceName( ) + { + return "com.sun.star.rendering.SpriteCanvas.OGL"; + } + + 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); + } + + void SpriteCanvas::renderRecordedActions() const + { + maCanvasHelper.renderRecordedActions(); + } + +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_rendering_SpriteCanvas_OGL_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args) +{ + if( !OpenGLHelper::supportsOpenGL()) + return nullptr; + rtl::Reference<oglcanvas::SpriteCanvas> p = new oglcanvas::SpriteCanvas(args, context); + cppu::acquire(p.get()); + p->initialize(); + return static_cast<cppu::OWeakObject*>(p.get()); +} + +/* 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 000000000..33948b2d0 --- /dev/null +++ b/canvas/source/opengl/ogl_spritecanvas.hxx @@ -0,0 +1,114 @@ +/* -*- 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/. + */ + +#pragma once + +#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/compbase.hxx> +#include <comphelper/uno3.hxx> + +#include <base/canvasbase.hxx> +#include <base/disambiguationhelper.hxx> +#include <base/bufferedgraphicdevicebase.hxx> + +#include "ogl_spritedevicehelper.hxx" +#include "ogl_canvashelper.hxx" + + +namespace oglcanvas +{ + class CanvasCustomSprite; + + typedef ::cppu::WeakComponentImplHelper< css::rendering::XSpriteCanvas, + css::rendering::XGraphicDevice, + css::lang::XMultiServiceFactory, + css::rendering::XBufferController, + css::awt::XWindowListener, + css::util::XUpdatable, + css::beans::XPropertySet, + css::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 css::uno::Sequence< + css::uno::Any >& aArguments, + const css::uno::Reference< + css::uno::XComponentContext >& rxContext ); + + void initialize(); + + /// Dispose all internal references + virtual void disposeThis() override; + + // 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 ) override; + virtual sal_Bool SAL_CALL switchBuffer( sal_Bool bUpdateAll ) override; + + // XSpriteCanvas + virtual css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL createSpriteFromAnimation( const css::uno::Reference< css::rendering::XAnimation >& animation ) override; + virtual css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL createSpriteFromBitmaps( const css::uno::Sequence< css::uno::Reference< css::rendering::XBitmap > >& animationBitmaps, ::sal_Int8 interpolationMode ) override; + virtual css::uno::Reference< css::rendering::XCustomSprite > SAL_CALL createCustomSprite( const css::geometry::RealSize2D& spriteSize ) override; + virtual css::uno::Reference< css::rendering::XSprite > SAL_CALL createClonedSprite( const css::uno::Reference< css::rendering::XSprite >& original ) override; + virtual sal_Bool SAL_CALL updateScreen( sal_Bool bUpdateAll ) override; + + // XServiceName + virtual OUString SAL_CALL getServiceName( ) override; + + void show( const ::rtl::Reference< CanvasCustomSprite >& ); + void hide( const ::rtl::Reference< CanvasCustomSprite >& ); + + /** Write out recorded actions + */ + void renderRecordedActions() const; + + private: + css::uno::Sequence< css::uno::Any > maArguments; + }; + + typedef ::rtl::Reference< SpriteCanvas > SpriteCanvasRef; +} + +/* 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 000000000..8cb0a6934 --- /dev/null +++ b/canvas/source/opengl/ogl_spritedevicehelper.cxx @@ -0,0 +1,549 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/utils/canvastools.hxx> +#include <basegfx/utils/unopolypolygon.hxx> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/opengl/OpenGLHelper.hxx> +#include <vcl/syschild.hxx> + +#include "ogl_spritedevicehelper.hxx" +#include "ogl_spritecanvas.hxx" +#include "ogl_canvasbitmap.hxx" +#include "ogl_canvastools.hxx" +#include "ogl_canvascustomsprite.hxx" +#include "ogl_texturecache.hxx" + +using namespace ::com::sun::star; + +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) +{ + // use whole window + glViewport( 0,0, + static_cast<GLsizei>(rSize.Width()), + static_cast<GLsizei>(rSize.Height()) ); + + // model coordinate system is already in device pixel + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslated(-1.0, 1.0, 0.0); + glScaled( 2.0 / rSize.Width(), + -2.0 / rSize.Height(), + 1.0 ); + + // clear to black + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +namespace oglcanvas +{ + + SpriteDeviceHelper::SpriteDeviceHelper() : + mpSpriteCanvas(nullptr), + mpTextureCache(std::make_shared<TextureCache>()), + mnLinearTwoColorGradientProgram(0), + mnLinearMultiColorGradientProgram(0), + mnRadialTwoColorGradientProgram(0), + mnRadialMultiColorGradientProgram(0), + mnRectangularTwoColorGradientProgram(0), + mnRectangularMultiColorGradientProgram(0), + mxContext(OpenGLContext::Create()) + {} + + SpriteDeviceHelper::~SpriteDeviceHelper() + { mxContext->dispose(); } + + void SpriteDeviceHelper::init( vcl::Window& rWindow, + SpriteCanvas& rSpriteCanvas, + const awt::Rectangle& rViewArea ) + { + mpSpriteCanvas = &rSpriteCanvas; + + rSpriteCanvas.setWindow( + uno::Reference<awt::XWindow2>( + VCLUnoHelper::GetInterface(&rWindow), + uno::UNO_QUERY_THROW) ); + + mxContext->requestLegacyContext(); + mxContext->init(&rWindow); + // init window context + initContext(); + + mnLinearMultiColorGradientProgram = + OpenGLHelper::LoadShaders("dummyVertexShader", "linearMultiColorGradientFragmentShader"); + + mnLinearTwoColorGradientProgram = + OpenGLHelper::LoadShaders("dummyVertexShader", "linearTwoColorGradientFragmentShader"); + + mnRadialMultiColorGradientProgram = + OpenGLHelper::LoadShaders("dummyVertexShader", "radialMultiColorGradientFragmentShader"); + + mnRadialTwoColorGradientProgram = + OpenGLHelper::LoadShaders("dummyVertexShader", "radialTwoColorGradientFragmentShader"); + + mnRectangularMultiColorGradientProgram = + OpenGLHelper::LoadShaders("dummyVertexShader", "rectangularMultiColorGradientFragmentShader"); + + mnRectangularTwoColorGradientProgram = + OpenGLHelper::LoadShaders("dummyVertexShader", "rectangularTwoColorGradientFragmentShader"); + + mxContext->makeCurrent(); + + notifySizeUpdate(rViewArea); + // TODO(E3): check for GL_ARB_imaging extension + } + + void SpriteDeviceHelper::disposing() + { + // release all references + mpSpriteCanvas = nullptr; + mpTextureCache.reset(); + + if( mxContext->isInitialized() ) + { + glDeleteProgram( mnRectangularTwoColorGradientProgram ); + glDeleteProgram( mnRectangularMultiColorGradientProgram ); + glDeleteProgram( mnRadialTwoColorGradientProgram ); + glDeleteProgram( mnRadialMultiColorGradientProgram ); + glDeleteProgram( mnLinearTwoColorGradientProgram ); + glDeleteProgram( mnLinearMultiColorGradientProgram ); + } + } + + geometry::RealSize2D SpriteDeviceHelper::getPhysicalResolution() + { + if( !mxContext->isInitialized() ) + return ::canvas::tools::createInfiniteSize2D(); // we're disposed + + // Map a one-by-one millimeter box to pixel + SystemChildWindow* pChildWindow = mxContext->getChildWindow(); + const MapMode aOldMapMode( pChildWindow->GetMapMode() ); + pChildWindow->SetMapMode( MapMode(MapUnit::MapMM) ); + const Size aPixelSize( pChildWindow->LogicToPixel(Size(1,1)) ); + pChildWindow->SetMapMode( aOldMapMode ); + + return vcl::unotools::size2DFromSize( aPixelSize ); + } + + geometry::RealSize2D SpriteDeviceHelper::getPhysicalSize() + { + if( !mxContext->isInitialized() ) + return ::canvas::tools::createInfiniteSize2D(); // we're disposed + + // Map the pixel dimensions of the output window to millimeter + SystemChildWindow* pChildWindow = mxContext->getChildWindow(); + const MapMode aOldMapMode( pChildWindow->GetMapMode() ); + pChildWindow->SetMapMode( MapMode(MapUnit::MapMM) ); + const Size aLogSize( pChildWindow->PixelToLogic(pChildWindow->GetOutputSizePixel()) ); + pChildWindow->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 ) ); + } + + 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 ) ); + } + + uno::Reference< rendering::XVolatileBitmap > SpriteDeviceHelper::createVolatileAlphaBitmap( + const uno::Reference< rendering::XGraphicDevice >& /*rDevice*/, + const geometry::IntegerSize2D& /*size*/ ) + { + return uno::Reference< rendering::XVolatileBitmap >(); + } + + 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; + } + }; + } + + bool SpriteDeviceHelper::showBuffer( bool bIsVisible, SAL_UNUSED_PARAMETER bool /*bUpdateAll*/ ) + { + // hidden or disposed? + if( !bIsVisible || !mxContext->isInitialized() || !mpSpriteCanvas ) + return false; + + mxContext->makeCurrent(); + + SystemChildWindow* pChildWindow = mxContext->getChildWindow(); + const ::Size& rOutputSize = pChildWindow->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( + maActiveSprites.begin(), + maActiveSprites.end()); + std::sort(aSprites.begin(), + aSprites.end(), + SpriteComparator()); + for( const auto& rSprite : aSprites ) + rSprite->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 { fps, static_cast<double>(maActiveSprites.size()), + static_cast<double>(mpTextureCache->getCacheSize()), + static_cast<double>(mpTextureCache->getCacheMissCount()), + static_cast<double>(mpTextureCache->getCacheHitCount()) }; + renderOSD( aVec, 20 ); + + /* + * TODO: moggi: fix it! + // switch buffer, sync etc. + const unx::Window aXWindow=pChildWindow->GetSystemData()->aWindow; + unx::glXSwapBuffers(reinterpret_cast<unx::Display*>(mpDisplay), + aXWindow); + pChildWindow->Show(); + unx::glXWaitGL(); + XSync( reinterpret_cast<unx::Display*>(mpDisplay), false ); + */ + mxContext->swapBuffers(); + + // 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; + } + + bool SpriteDeviceHelper::switchBuffer( bool bIsVisible, bool bUpdateAll ) + { + // no difference for VCL canvas + return showBuffer( bIsVisible, bUpdateAll ); + } + + uno::Any SpriteDeviceHelper::isAccelerated() const + { + return css::uno::Any(false); + } + + uno::Any SpriteDeviceHelper::getDeviceHandle() const + { + const SystemChildWindow* pChildWindow = mxContext->getChildWindow(); + const OutputDevice* pDevice = pChildWindow ? pChildWindow->GetOutDev() : nullptr; + return uno::Any(reinterpret_cast<sal_Int64>(pDevice)); + } + + uno::Any SpriteDeviceHelper::getSurfaceHandle() const + { + return uno::Any(); + } + + uno::Reference<rendering::XColorSpace> SpriteDeviceHelper::getColorSpace() const + { + // always the same + return ::canvas::tools::getStdColorSpace(); + } + + void SpriteDeviceHelper::notifySizeUpdate( const awt::Rectangle& rBounds ) + { + if( mxContext->isInitialized() ) + { + SystemChildWindow* pChildWindow = mxContext->getChildWindow(); + pChildWindow->setPosSizePixel( + 0,0,rBounds.Width,rBounds.Height); + } + } + + void SpriteDeviceHelper::dumpScreenContent() const + { + SAL_INFO("canvas.ogl", __func__ ); + } + + 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); + } + + namespace + { + + class BufferContextImpl : public IBufferContext + { + GLuint mnFramebufferId; + GLuint mnDepthId; + GLuint mnTextureId; + + virtual void startBufferRendering() override + { + glBindFramebuffer(GL_FRAMEBUFFER, mnFramebufferId); + } + + virtual void endBufferRendering() override + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + virtual GLuint getTextureId() override + { + return mnTextureId; + } + + public: + explicit BufferContextImpl(const ::basegfx::B2IVector& rSize) : + mnFramebufferId(0), + mnDepthId(0), + mnTextureId(0) + { + OpenGLHelper::createFramebuffer(rSize.getX(), rSize.getY(), mnFramebufferId, + mnDepthId, mnTextureId); + } + + virtual ~BufferContextImpl() override + { + glDeleteTextures(1, &mnTextureId); + glDeleteRenderbuffers(1, &mnDepthId); + glDeleteFramebuffers(1, &mnFramebufferId); + } + }; + } + + IBufferContextSharedPtr SpriteDeviceHelper::createBufferContext(const ::basegfx::B2IVector& rSize) const + { + return std::make_shared<BufferContextImpl>(rSize); + } + + 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 000000000..3b9a26262 --- /dev/null +++ b/canvas/source/opengl/ogl_spritedevicehelper.hxx @@ -0,0 +1,135 @@ +/* -*- 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/. + */ + +#pragma once + +#include <vcl/opengl/OpenGLContext.hxx> + +#include <rtl/ref.hxx> +#include <canvas/elapsedtime.hxx> +#include <com/sun/star/rendering/XGraphicDevice.hpp> + +#include "ogl_buffercontext.hxx" + +#include <set> + +namespace vcl { class Window; } +class SystemChildWindow; +namespace basegfx{ class B2IVector; class B2DHomMatrix; } +namespace com::sun::star::awt { struct Rectangle; } +namespace com::sun::star::geometry { struct AffineMatrix2D; } + + +namespace oglcanvas +{ + class TextureCache; + class SpriteCanvas; + class CanvasCustomSprite; + class CanvasHelper; + + class SpriteDeviceHelper + { + public: + SpriteDeviceHelper(); + ~SpriteDeviceHelper(); + + /// make noncopyable + SpriteDeviceHelper(const SpriteDeviceHelper&) = delete; + const SpriteDeviceHelper& operator=(const SpriteDeviceHelper&) = delete; + + void init( vcl::Window& rWindow, + SpriteCanvas& rSpriteCanvas, + const css::awt::Rectangle& rViewArea ); + + /// Dispose all internal references + void disposing(); + + // XWindowGraphicDevice + css::geometry::RealSize2D getPhysicalResolution(); + css::geometry::RealSize2D getPhysicalSize(); + css::uno::Reference< css::rendering::XLinePolyPolygon2D > createCompatibleLinePolyPolygon( + const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice, + const css::uno::Sequence< css::uno::Sequence< css::geometry::RealPoint2D > >& points ); + css::uno::Reference< css::rendering::XBezierPolyPolygon2D > createCompatibleBezierPolyPolygon( + const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice, + const css::uno::Sequence< css::uno::Sequence< css::geometry::RealBezierSegment2D > >& points ); + css::uno::Reference< css::rendering::XBitmap > createCompatibleBitmap( + const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice, + const css::geometry::IntegerSize2D& size ); + css::uno::Reference< css::rendering::XVolatileBitmap > createVolatileBitmap( + const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice, + const css::geometry::IntegerSize2D& size ); + css::uno::Reference< css::rendering::XBitmap > createCompatibleAlphaBitmap( + const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice, + const css::geometry::IntegerSize2D& size ); + css::uno::Reference< css::rendering::XVolatileBitmap > createVolatileAlphaBitmap( + const css::uno::Reference< css::rendering::XGraphicDevice >& rDevice, + const css::geometry::IntegerSize2D& size ); + + bool showBuffer( bool bIsVisible, bool bUpdateAll ); + bool switchBuffer( bool bIsVisible, bool bUpdateAll ); + + css::uno::Any isAccelerated() const; + css::uno::Any getDeviceHandle() const; + css::uno::Any getSurfaceHandle() const; + css::uno::Reference< + css::rendering::XColorSpace > getColorSpace() const; + + void notifySizeUpdate( const css::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 css::rendering::ARGBColor* pColors, + const css::uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ); + /// enable radial gradient shader "texture" with given parameters + void useRadialGradientShader( const css::rendering::ARGBColor* pColors, + const css::uno::Sequence< double >& rStops, + const ::basegfx::B2DHomMatrix& rTexTransform ); + /// enable rectangular gradient shader "texture" with given parameters + void useRectangularGradientShader( const css::rendering::ARGBColor* pColors, + const css::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; + + private: + /// 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; + + std::shared_ptr<TextureCache> mpTextureCache; + + unsigned int mnLinearTwoColorGradientProgram; + unsigned int mnLinearMultiColorGradientProgram; + unsigned int mnRadialTwoColorGradientProgram; + unsigned int mnRadialMultiColorGradientProgram; + unsigned int mnRectangularTwoColorGradientProgram; + unsigned int mnRectangularMultiColorGradientProgram; + + rtl::Reference<OpenGLContext> mxContext; + }; +} + +/* 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 000000000..5eab3d9bc --- /dev/null +++ b/canvas/source/opengl/ogl_textlayout.cxx @@ -0,0 +1,180 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> +#include <utility> + +#include <tools/diagnose_ex.h> + +#include "ogl_textlayout.hxx" + +using namespace ::com::sun::star; + +namespace oglcanvas +{ + TextLayout::TextLayout( rendering::StringContext aText, + sal_Int8 nDirection, + sal_Int64 /*nRandomSeed*/, + CanvasFont::ImplRef rFont ) : + TextLayoutBaseT( m_aMutex ), + maText(std::move( aText )), + mpFont(std::move( rFont )), + mnTextDirection( nDirection ) + { + } + + void SAL_CALL TextLayout::disposing() + { + mpFont.clear(); + } + + // XTextLayout + uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > > SAL_CALL TextLayout::queryTextShapes( ) + { + // TODO + return uno::Sequence< uno::Reference< rendering::XPolyPolygon2D > >(); + } + + uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryInkMeasures( ) + { + // TODO + return uno::Sequence< geometry::RealRectangle2D >(); + } + + uno::Sequence< geometry::RealRectangle2D > SAL_CALL TextLayout::queryMeasures( ) + { + // TODO + return uno::Sequence< geometry::RealRectangle2D >(); + } + + uno::Sequence< double > SAL_CALL TextLayout::queryLogicalAdvancements( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return maLogicalAdvancements; + } + + void SAL_CALL TextLayout::applyLogicalAdvancements( const uno::Sequence< double >& aAdvancements ) + { + ::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( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ENSURE_OR_THROW( mpFont, + "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.hasElements() ) + { + 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*/ ) + { + // TODO + return 0.0; + } + + double SAL_CALL TextLayout::combinedJustify( const uno::Sequence< uno::Reference< rendering::XTextLayout > >& /*aNextLayouts*/, + double /*nSize*/ ) + { + // TODO + return 0.0; + } + + rendering::TextHit SAL_CALL TextLayout::getTextHit( const geometry::RealPoint2D& /*aHitPoint*/ ) + { + // TODO + return rendering::TextHit(); + } + + rendering::Caret SAL_CALL TextLayout::getCaret( sal_Int32 /*nInsertionIndex*/, + sal_Bool /*bExcludeLigatures*/ ) + { + // TODO + return rendering::Caret(); + } + + sal_Int32 SAL_CALL TextLayout::getNextInsertionIndex( sal_Int32 /*nStartIndex*/, + sal_Int32 /*nCaretAdvancement*/, + sal_Bool /*bExcludeLigatures*/ ) + { + // TODO + return 0; + } + + uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryVisualHighlighting( sal_Int32 /*nStartIndex*/, + sal_Int32 /*nEndIndex*/ ) + { + // TODO + return uno::Reference< rendering::XPolyPolygon2D >(); + } + + uno::Reference< rendering::XPolyPolygon2D > SAL_CALL TextLayout::queryLogicalHighlighting( sal_Int32 /*nStartIndex*/, + sal_Int32 /*nEndIndex*/ ) + { + // TODO + return uno::Reference< rendering::XPolyPolygon2D >(); + } + + double SAL_CALL TextLayout::getBaselineOffset( ) + { + // TODO + return 0.0; + } + + sal_Int8 SAL_CALL TextLayout::getMainTextDirection( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return mnTextDirection; + } + + uno::Reference< rendering::XCanvasFont > SAL_CALL TextLayout::getFont( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return mpFont; + } + + rendering::StringContext SAL_CALL TextLayout::getText( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return maText; + } + +} + +/* 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 000000000..ce7b19589 --- /dev/null +++ b/canvas/source/opengl/ogl_textlayout.hxx @@ -0,0 +1,70 @@ +/* -*- 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/. + */ + +#pragma once + +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> + +#include <com/sun/star/rendering/XTextLayout.hpp> + +#include "ogl_canvasfont.hxx" + + +/* Definition of TextLayout class */ + +namespace oglcanvas +{ + typedef ::cppu::WeakComponentImplHelper< css::rendering::XTextLayout > TextLayoutBaseT; + + class TextLayout : public ::cppu::BaseMutex, + public TextLayoutBaseT + { + public: + TextLayout( css::rendering::StringContext aText, + sal_Int8 nDirection, + sal_Int64 nRandomSeed, + CanvasFont::ImplRef rFont ); + + /// make noncopyable + TextLayout(const TextLayout&) = delete; + const TextLayout& operator=(const TextLayout&) = delete; + + /// Dispose all internal references + virtual void SAL_CALL disposing() override; + + // XTextLayout + virtual css::uno::Sequence< css::uno::Reference< css::rendering::XPolyPolygon2D > > SAL_CALL queryTextShapes( ) override; + virtual css::uno::Sequence< css::geometry::RealRectangle2D > SAL_CALL queryInkMeasures( ) override; + virtual css::uno::Sequence< css::geometry::RealRectangle2D > SAL_CALL queryMeasures( ) override; + virtual css::uno::Sequence< double > SAL_CALL queryLogicalAdvancements( ) override; + virtual void SAL_CALL applyLogicalAdvancements( const css::uno::Sequence< double >& aAdvancements ) override; + virtual css::geometry::RealRectangle2D SAL_CALL queryTextBounds( ) override; + virtual double SAL_CALL justify( double nSize ) override; + virtual double SAL_CALL combinedJustify( const css::uno::Sequence< css::uno::Reference< css::rendering::XTextLayout > >& aNextLayouts, double nSize ) override; + virtual css::rendering::TextHit SAL_CALL getTextHit( const css::geometry::RealPoint2D& aHitPoint ) override; + virtual css::rendering::Caret SAL_CALL getCaret( sal_Int32 nInsertionIndex, sal_Bool bExcludeLigatures ) override; + virtual sal_Int32 SAL_CALL getNextInsertionIndex( sal_Int32 nStartIndex, sal_Int32 nCaretAdvancement, sal_Bool bExcludeLigatures ) override; + virtual css::uno::Reference< css::rendering::XPolyPolygon2D > SAL_CALL queryVisualHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override; + virtual css::uno::Reference< css::rendering::XPolyPolygon2D > SAL_CALL queryLogicalHighlighting( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override; + virtual double SAL_CALL getBaselineOffset( ) override; + virtual sal_Int8 SAL_CALL getMainTextDirection( ) override; + virtual css::uno::Reference< css::rendering::XCanvasFont > SAL_CALL getFont( ) override; + virtual css::rendering::StringContext SAL_CALL getText( ) override; + + private: + css::rendering::StringContext maText; + css::uno::Sequence< double > maLogicalAdvancements; + CanvasFont::ImplRef mpFont; + sal_Int8 mnTextDirection; + }; + +} + +/* 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 000000000..43fb7d8e2 --- /dev/null +++ b/canvas/source/opengl/ogl_texturecache.cxx @@ -0,0 +1,117 @@ +/* -*- 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 <sal/config.h> + +#include <epoxy/gl.h> + +#include <com/sun/star/geometry/IntegerSize2D.hpp> + +#include "ogl_texturecache.hxx" + + +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 + for( const auto& rCache : maCache ) + { + glDeleteTextures( 1, &rCache.second.nTexture ); + } + + 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" + const TextureCacheMapT::const_iterator aEnd = maCache.end(); + for( auto aCurr = maCache.begin(); aCurr != aEnd; /* increment managed in loop */) + { + if( aCurr->second.bOld ) + { + glDeleteTextures( 1, &aCurr->second.nTexture ); + aCurr = maCache.erase( aCurr ); + } + else + { + aCurr->second.bOld = true; + ++aCurr; + } + } + + 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 000000000..8d0a425ce --- /dev/null +++ b/canvas/source/opengl/ogl_texturecache.hxx @@ -0,0 +1,60 @@ +/* -*- 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/. + */ + +#pragma once + +#include <sal/types.h> +#include <unordered_map> + +namespace com::sun::star::geometry { struct IntegerSize2D; } + + +namespace oglcanvas +{ + class TextureCache + { + public: + TextureCache(); + ~TextureCache(); + + /// clear whole cache, reset statistic counters + void flush(); + + /** prune old entries from cache + + Every time 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 css::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; + }; +} + +/* 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 000000000..8f42f734e --- /dev/null +++ b/canvas/source/opengl/ogl_tools.hxx @@ -0,0 +1,27 @@ +/* -*- 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/. + */ + +#pragma once + +#include <sal/config.h> +#include <epoxy/gl.h> + +namespace oglcanvas +{ + struct TransformationPreserver + { + TransformationPreserver() + { glPushMatrix(); } + + ~TransformationPreserver() + { glPopMatrix(); } + }; +} + +/* 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 000000000..f6c9af43f --- /dev/null +++ b/canvas/source/opengl/oglcanvas.component @@ -0,0 +1,17 @@ +<?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" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.rendering.SpriteCanvas.OGL" + constructor="com_sun_star_comp_rendering_SpriteCanvas_OGL_get_implementation"> + <service name="com.sun.star.rendering.SpriteCanvas.OGL"/> + </implementation> +</component> |