1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <vcl/opengl/OpenGLContext.hxx>
22 #include <vcl/opengl/OpenGLHelper.hxx>
26 #include <vcl/salbtype.hxx>
27 #include <vcl/pngwrite.hxx>
29 #include "opengl/framebuffer.hxx"
30 #include "opengl/texture.hxx"
31 #include "opengl/zone.hxx"
32 #include "opengl/RenderState.hxx"
37 SAL_CONSTEXPR GLenum constInternalFormat
= GL_RGBA8
;
39 } // end anonymous namespace
41 // texture with allocated size
42 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth
, int nHeight
, bool bAllocate
) :
47 mnFilter( GL_NEAREST
),
50 OpenGLVCLContextZone aContextZone
;
52 auto& rState
= OpenGLContext::getVCLContext()->state();
53 TextureState::generate(mnTexture
);
54 rState
->texture().active(0);
55 rState
->texture().bind(mnTexture
);
57 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
59 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
61 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
63 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
67 glTexImage2D( GL_TEXTURE_2D
, 0, constInternalFormat
, nWidth
, nHeight
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, nullptr );
71 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " " << nWidth
<< "x" << nHeight
<< " allocate" );
74 // texture with content retrieved from FBO
75 ImplOpenGLTexture::ImplOpenGLTexture( int nX
, int nY
, int nWidth
, int nHeight
) :
80 mnFilter( GL_NEAREST
),
83 OpenGLVCLContextZone aContextZone
;
85 // FIXME We need the window height here
86 // nY = GetHeight() - nHeight - nY;
88 auto& rState
= OpenGLContext::getVCLContext()->state();
89 TextureState::generate(mnTexture
);
90 rState
->texture().active(0);
91 rState
->texture().bind(mnTexture
);
93 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
95 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
97 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
99 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
101 glCopyTexImage2D( GL_TEXTURE_2D
, 0, constInternalFormat
, nX
, nY
, nWidth
, nHeight
, 0 );
104 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " " << nWidth
<< "x" << nHeight
<< " from x" << nX
<< ", y" << nY
);
107 // texture from buffer data
108 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth
, int nHeight
, int nFormat
, int nType
, void const * pData
) :
113 mnFilter( GL_NEAREST
),
116 OpenGLVCLContextZone aContextZone
;
118 auto& rState
= OpenGLContext::getVCLContext()->state();
119 TextureState::generate(mnTexture
);
120 rState
->texture().active(0);
121 rState
->texture().bind(mnTexture
);
123 glPixelStorei( GL_UNPACK_ALIGNMENT
, 1 );
125 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
127 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
129 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
131 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
133 glTexImage2D( GL_TEXTURE_2D
, 0, constInternalFormat
, mnWidth
, mnHeight
, 0, nFormat
, nType
, pData
);
136 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " " << nWidth
<< "x" << nHeight
<< " from data" );
139 GLuint
ImplOpenGLTexture::AddStencil()
141 assert( mnOptStencil
== 0 );
143 glGenRenderbuffers( 1, &mnOptStencil
);
145 glBindRenderbuffer( GL_RENDERBUFFER
, mnOptStencil
);
147 VCL_GL_INFO( "Allocate stencil " << mnWidth
<< " x " << mnHeight
);
148 glRenderbufferStorage( GL_RENDERBUFFER
, GL_STENCIL_INDEX
,
151 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
157 ImplOpenGLTexture::~ImplOpenGLTexture()
159 VCL_GL_INFO( "~OpenGLTexture " << mnTexture
);
163 void ImplOpenGLTexture::Dispose()
167 // During shutdown GL is already de-initialized, so we should not try to create a new context.
169 rtl::Reference
<OpenGLContext
> xContext
= OpenGLContext::getVCLContext(false);
172 // FIXME: this is really not optimal performance-wise.
174 // Check we have been correctly un-bound from all framebuffers.
175 ImplSVData
* pSVData
= ImplGetSVData();
176 rtl::Reference
<OpenGLContext
> pContext
= pSVData
->maGDIData
.mpLastContext
;
180 pContext
->makeCurrent();
181 pContext
->UnbindTextureFromFramebuffers( mnTexture
);
184 if( mnOptStencil
!= 0 )
186 glDeleteRenderbuffers( 1, &mnOptStencil
);
189 auto& rState
= pContext
->state();
190 rState
->texture().unbindAndDelete(mnTexture
);
201 bool ImplOpenGLTexture::InsertBuffer(int nX
, int nY
, int nWidth
, int nHeight
, int nFormat
, int nType
, sal_uInt8
* pData
)
203 if (!pData
|| mnTexture
== 0)
206 rtl::Reference
<OpenGLContext
> xContext
= OpenGLContext::getVCLContext();
207 xContext
->state()->texture().active(0);
208 xContext
->state()->texture().bind(mnTexture
);
210 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
212 glTexSubImage2D(GL_TEXTURE_2D
, 0, nX
, mnHeight
- nY
- nHeight
, nWidth
, nHeight
, nFormat
, nType
, pData
);
215 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " Insert buff. to " << nX
<< " " << nY
216 << " size " << nWidth
<< "x" << nHeight
<< " from data" );
221 bool ImplOpenGLTexture::InitializeSlotMechanism(int nInitialSlotSize
)
223 if (mpSlotReferences
)
226 mpSlotReferences
.reset(new std::vector
<int>(nInitialSlotSize
, 0));
230 void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber
)
233 if (mpSlotReferences
&& nSlotNumber
>= 0)
235 if (nSlotNumber
>= int(mpSlotReferences
->size()))
236 mpSlotReferences
->resize(nSlotNumber
+ 1, 0);
238 mpSlotReferences
->at(nSlotNumber
)++;
242 void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber
)
244 if (mpSlotReferences
&& nSlotNumber
>= 0)
246 if (nSlotNumber
>= int(mpSlotReferences
->size()))
247 mpSlotReferences
->resize(nSlotNumber
, 0);
249 mpSlotReferences
->at(nSlotNumber
)--;
251 if (mpSlotReferences
->at(nSlotNumber
) == 0 && mFunctSlotDeallocateCallback
)
253 mFunctSlotDeallocateCallback(nSlotNumber
);
263 OpenGLTexture::OpenGLTexture() :
264 maRect( 0, 0, 0, 0 ),
270 OpenGLTexture::OpenGLTexture(ImplOpenGLTexture
* pImpl
, Rectangle aRectangle
, int nSlotNumber
)
273 , mnSlotNumber(nSlotNumber
)
276 mpImpl
->IncreaseRefCount(nSlotNumber
);
279 OpenGLTexture::OpenGLTexture( int nWidth
, int nHeight
, bool bAllocate
)
280 : maRect( Point( 0, 0 ), Size( nWidth
, nHeight
) )
283 mpImpl
= new ImplOpenGLTexture( nWidth
, nHeight
, bAllocate
);
286 OpenGLTexture::OpenGLTexture( int nX
, int nY
, int nWidth
, int nHeight
)
287 : maRect( Point( 0, 0 ), Size( nWidth
, nHeight
) )
290 mpImpl
= new ImplOpenGLTexture( nX
, nY
, nWidth
, nHeight
);
293 OpenGLTexture::OpenGLTexture( int nWidth
, int nHeight
, int nFormat
, int nType
, void const * pData
)
294 : maRect( Point( 0, 0 ), Size( nWidth
, nHeight
) )
297 mpImpl
= new ImplOpenGLTexture( nWidth
, nHeight
, nFormat
, nType
, pData
);
300 OpenGLTexture::OpenGLTexture( const OpenGLTexture
& rTexture
)
302 maRect
= rTexture
.maRect
;
303 mpImpl
= rTexture
.mpImpl
;
304 mnSlotNumber
= rTexture
.mnSlotNumber
;
307 mpImpl
->IncreaseRefCount(mnSlotNumber
);
310 OpenGLTexture::OpenGLTexture( const OpenGLTexture
& rTexture
,
311 int nX
, int nY
, int nWidth
, int nHeight
)
313 maRect
= Rectangle( Point( rTexture
.maRect
.Left() + nX
, rTexture
.maRect
.Top() + nY
),
314 Size( nWidth
, nHeight
) );
315 mpImpl
= rTexture
.mpImpl
;
316 mnSlotNumber
= rTexture
.mnSlotNumber
;
318 mpImpl
->IncreaseRefCount(mnSlotNumber
);
319 VCL_GL_INFO( "Copying texture " << Id() << " [" << maRect
.Left() << "," << maRect
.Top() << "] " << GetWidth() << "x" << GetHeight() );
322 OpenGLTexture::~OpenGLTexture()
325 mpImpl
->DecreaseRefCount(mnSlotNumber
);
328 bool OpenGLTexture::IsUnique() const
330 return mpImpl
== nullptr || mpImpl
->IsUnique();
333 GLuint
OpenGLTexture::Id() const
336 return mpImpl
->mnTexture
;
340 int OpenGLTexture::GetWidth() const
342 return maRect
.GetWidth();
345 int OpenGLTexture::GetHeight() const
347 return maRect
.GetHeight();
350 GLuint
OpenGLTexture::StencilId() const
352 return mpImpl
? mpImpl
->mnOptStencil
: 0;
355 GLuint
OpenGLTexture::AddStencil()
358 return mpImpl
->AddStencil();
363 void OpenGLTexture::GetCoord( GLfloat
* pCoord
, const SalTwoRect
& rPosAry
, bool bInverted
) const
365 VCL_GL_INFO( "Getting coord " << Id() << " [" << maRect
.Left() << "," << maRect
.Top() << "] " << GetWidth() << "x" << GetHeight() );
367 if( mpImpl
== nullptr )
369 pCoord
[0] = pCoord
[1] = pCoord
[2] = pCoord
[3] = 0.0f
;
370 pCoord
[4] = pCoord
[5] = pCoord
[6] = pCoord
[7] = 0.0f
;
374 pCoord
[0] = pCoord
[2] = (maRect
.Left() + rPosAry
.mnSrcX
) / (double) mpImpl
->mnWidth
;
375 pCoord
[4] = pCoord
[6] = (maRect
.Left() + rPosAry
.mnSrcX
+ rPosAry
.mnSrcWidth
) / (double) mpImpl
->mnWidth
;
379 pCoord
[3] = pCoord
[5] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
) / (double) mpImpl
->mnHeight
;
380 pCoord
[1] = pCoord
[7] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
+ rPosAry
.mnSrcHeight
) / (double) mpImpl
->mnHeight
;
384 pCoord
[1] = pCoord
[7] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
) / (double) mpImpl
->mnHeight
;
385 pCoord
[3] = pCoord
[5] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
+ rPosAry
.mnSrcHeight
) / (double) mpImpl
->mnHeight
;
390 void OpenGLTexture::FillCoords
<GL_TRIANGLES
>(std::vector
<GLfloat
>& aCoord
, const SalTwoRect
& rPosAry
, bool bInverted
) const
392 VCL_GL_INFO("Add coord " << Id() << " [" << maRect
.Left() << "," << maRect
.Top() << "] " << GetWidth() << "x" << GetHeight() );
393 VCL_GL_INFO(" With 2Rect Src [" << rPosAry
.mnSrcX
<< ", " << rPosAry
.mnSrcY
<< "] wh (" << rPosAry
.mnSrcWidth
<< ", " << rPosAry
.mnSrcHeight
<< ")");
394 VCL_GL_INFO(" With 2Rect Dest [" << rPosAry
.mnDestX
<< ", " << rPosAry
.mnDestY
<< "] wh (" << rPosAry
.mnDestWidth
<< ", " << rPosAry
.mnDestHeight
<< ")");
403 double fTextureWidth(mpImpl
->mnWidth
);
404 double fTextureHeight(mpImpl
->mnHeight
);
406 x1
= (maRect
.Left() + rPosAry
.mnSrcX
) / fTextureWidth
;
407 x2
= (maRect
.Left() + rPosAry
.mnSrcX
+ rPosAry
.mnSrcWidth
) / fTextureWidth
;
411 y2
= 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
) / fTextureHeight
;
412 y1
= 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
+ rPosAry
.mnSrcHeight
) / fTextureHeight
;
416 y1
= 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
) / fTextureHeight
;
417 y2
= 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
+ rPosAry
.mnSrcHeight
) / fTextureHeight
;
421 aCoord
.push_back(x1
);
422 aCoord
.push_back(y1
);
424 aCoord
.push_back(x2
);
425 aCoord
.push_back(y1
);
427 aCoord
.push_back(x1
);
428 aCoord
.push_back(y2
);
430 aCoord
.push_back(x1
);
431 aCoord
.push_back(y2
);
433 aCoord
.push_back(x2
);
434 aCoord
.push_back(y1
);
436 aCoord
.push_back(x2
);
437 aCoord
.push_back(y2
);
440 void OpenGLTexture::GetWholeCoord( GLfloat
* pCoord
) const
442 if( GetWidth() != mpImpl
->mnWidth
|| GetHeight() != mpImpl
->mnHeight
)
444 pCoord
[0] = pCoord
[2] = maRect
.Left() / (double) mpImpl
->mnWidth
;
445 pCoord
[4] = pCoord
[6] = maRect
.Right() / (double) mpImpl
->mnWidth
;
446 pCoord
[3] = pCoord
[5] = 1.0f
- maRect
.Top() / (double) mpImpl
->mnHeight
;
447 pCoord
[1] = pCoord
[7] = 1.0f
- maRect
.Bottom() / (double) mpImpl
->mnHeight
;
451 pCoord
[0] = pCoord
[2] = 0;
452 pCoord
[4] = pCoord
[6] = 1;
453 pCoord
[1] = pCoord
[7] = 0;
454 pCoord
[3] = pCoord
[5] = 1;
458 OpenGLTexture
OpenGLTexture::GetWholeTexture()
461 return OpenGLTexture(mpImpl
, Rectangle(Point(0, 0), Size(mpImpl
->mnWidth
, mpImpl
->mnHeight
)), -1);
462 return OpenGLTexture();
465 GLenum
OpenGLTexture::GetFilter() const
468 return mpImpl
->mnFilter
;
472 bool OpenGLTexture::CopyData(int nWidth
, int nHeight
, int nFormat
, int nType
, sal_uInt8
* pData
)
474 if (!pData
|| mpImpl
== nullptr)
477 int nX
= maRect
.Left();
478 int nY
= maRect
.Top();
480 return mpImpl
->InsertBuffer(nX
, nY
, nWidth
, nHeight
, nFormat
, nType
, pData
);
483 void OpenGLTexture::SetFilter( GLenum nFilter
)
487 mpImpl
->mnFilter
= nFilter
;
488 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, nFilter
);
490 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, nFilter
);
495 void OpenGLTexture::Bind()
499 std::unique_ptr
<RenderState
>& rState
= OpenGLContext::getVCLContext()->state();
500 rState
->texture().bind(mpImpl
->mnTexture
);
503 VCL_GL_INFO( "OpenGLTexture::Binding invalid texture" );
508 void OpenGLTexture::Unbind()
512 std::unique_ptr
<RenderState
>& rState
= OpenGLContext::getVCLContext()->state();
513 rState
->texture().unbind(mpImpl
->mnTexture
);
517 void OpenGLTexture::SaveToFile(const OUString
& rFileName
)
519 std::vector
<sal_uInt8
> aBuffer(GetWidth() * GetHeight() * 4);
520 Read(GL_BGRA
, GL_UNSIGNED_BYTE
, aBuffer
.data());
521 BitmapEx aBitmap
= OpenGLHelper::ConvertBGRABufferToBitmapEx(aBuffer
.data(), GetWidth(), GetHeight());
524 vcl::PNGWriter
aWriter(aBitmap
);
525 SvFileStream
sOutput(rFileName
, StreamMode::WRITE
);
526 aWriter
.Write(sOutput
);
531 SAL_WARN("vcl.opengl", "Error writing png to " << rFileName
);
535 void OpenGLTexture::Read( GLenum nFormat
, GLenum nType
, sal_uInt8
* pData
)
537 if( mpImpl
== nullptr )
539 SAL_WARN( "vcl.opengl", "Can't read invalid texture" );
543 OpenGLVCLContextZone aContextZone
;
545 VCL_GL_INFO( "Reading texture " << Id() << " " << GetWidth() << "x" << GetHeight() );
547 if( GetWidth() == mpImpl
->mnWidth
&& GetHeight() == mpImpl
->mnHeight
)
550 glPixelStorei( GL_PACK_ALIGNMENT
, 1 );
552 // XXX: Call not available with GLES 2.0
553 glGetTexImage( GL_TEXTURE_2D
, 0, nFormat
, nType
, pData
);
559 long nWidth
= maRect
.GetWidth();
560 long nHeight
= maRect
.GetHeight();
561 long nX
= maRect
.Left();
562 long nY
= mpImpl
->mnHeight
- maRect
.Top() - nHeight
;
564 // Retrieve current context
565 ImplSVData
* pSVData
= ImplGetSVData();
566 rtl::Reference
<OpenGLContext
> pContext
= pSVData
->maGDIData
.mpLastContext
;
567 OpenGLFramebuffer
* pFramebuffer
= pContext
->AcquireFramebuffer(*this);
568 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
570 glReadPixels(nX
, nY
, nWidth
, nHeight
, nFormat
, nType
, pData
);
572 OpenGLContext::ReleaseFramebuffer(pFramebuffer
);
576 OpenGLTexture::operator bool() const
578 return ( mpImpl
!= nullptr );
581 OpenGLTexture
& OpenGLTexture::operator=( const OpenGLTexture
& rTexture
)
585 rTexture
.mpImpl
->IncreaseRefCount(rTexture
.mnSlotNumber
);
589 mpImpl
->DecreaseRefCount(mnSlotNumber
);
591 maRect
= rTexture
.maRect
;
592 mpImpl
= rTexture
.mpImpl
;
593 mnSlotNumber
= rTexture
.mnSlotNumber
;
598 bool OpenGLTexture::operator==( const OpenGLTexture
& rTexture
) const
600 return (mpImpl
== rTexture
.mpImpl
601 && maRect
== rTexture
.maRect
602 && mnSlotNumber
== rTexture
.mnSlotNumber
);
605 bool OpenGLTexture::operator!=( const OpenGLTexture
& rTexture
) const
607 return !( *this == rTexture
);
610 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */