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 <sal/log.hxx>
22 #include <tools/stream.hxx>
23 #include <vcl/opengl/OpenGLContext.hxx>
24 #include <vcl/opengl/OpenGLHelper.hxx>
28 #include <vcl/pngwrite.hxx>
30 #include <opengl/framebuffer.hxx>
31 #include <opengl/texture.hxx>
32 #include <opengl/zone.hxx>
33 #include <opengl/RenderState.hxx>
38 constexpr GLenum constInternalFormat
= GL_RGBA8
;
40 } // end anonymous namespace
42 // texture with allocated size
43 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
);
68 std::vector
< sal_uInt8
> buffer
;
69 buffer
.resize( nWidth
* nHeight
* 4 );
70 for( size_t i
= 0; i
< size_t( nWidth
* nHeight
); ++i
)
71 { // pre-fill the texture with deterministic garbage
72 bool odd
= (i
& 0x01);
73 buffer
[ i
* 4 ] = odd
? 0x40 : 0xBF;
74 buffer
[ i
* 4 + 1 ] = 0x80;
75 buffer
[ i
* 4 + 2 ] = odd
? 0xBF : 0x40;
76 buffer
[ i
* 4 + 3 ] = 0xFF;
78 glTexImage2D( GL_TEXTURE_2D
, 0, constInternalFormat
, nWidth
, nHeight
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, buffer
.data());
80 glTexImage2D( GL_TEXTURE_2D
, 0, constInternalFormat
, nWidth
, nHeight
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, nullptr );
85 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " " << nWidth
<< "x" << nHeight
<< " allocate" );
88 // texture with content retrieved from FBO
89 ImplOpenGLTexture::ImplOpenGLTexture( int nX
, int nY
, int nWidth
, int nHeight
) :
93 mnFilter( GL_NEAREST
),
96 OpenGLVCLContextZone aContextZone
;
98 // FIXME We need the window height here
99 // nY = GetHeight() - nHeight - nY;
101 auto& rState
= OpenGLContext::getVCLContext()->state();
102 TextureState::generate(mnTexture
);
103 rState
.texture().active(0);
104 rState
.texture().bind(mnTexture
);
106 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
108 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
110 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
112 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
114 glCopyTexImage2D( GL_TEXTURE_2D
, 0, constInternalFormat
, nX
, nY
, nWidth
, nHeight
, 0 );
117 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " " << nWidth
<< "x" << nHeight
<< " from x" << nX
<< ", y" << nY
);
120 // texture from buffer data
121 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth
, int nHeight
, int nFormat
, int nType
, void const * pData
) :
125 mnFilter( GL_NEAREST
),
128 OpenGLVCLContextZone aContextZone
;
130 auto& rState
= OpenGLContext::getVCLContext()->state();
131 TextureState::generate(mnTexture
);
132 rState
.texture().active(0);
133 rState
.texture().bind(mnTexture
);
135 glPixelStorei( GL_UNPACK_ALIGNMENT
, 1 );
137 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
139 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
141 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
143 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
145 glTexImage2D( GL_TEXTURE_2D
, 0, constInternalFormat
, mnWidth
, mnHeight
, 0, nFormat
, nType
, pData
);
148 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " " << nWidth
<< "x" << nHeight
<< " from data" );
151 GLuint
ImplOpenGLTexture::AddStencil()
153 assert( mnOptStencil
== 0 );
155 glGenRenderbuffers( 1, &mnOptStencil
);
157 glBindRenderbuffer( GL_RENDERBUFFER
, mnOptStencil
);
159 VCL_GL_INFO( "Allocate stencil " << mnWidth
<< " x " << mnHeight
);
160 glRenderbufferStorage( GL_RENDERBUFFER
, GL_STENCIL_INDEX
,
163 glBindRenderbuffer(GL_RENDERBUFFER
, 0);
169 ImplOpenGLTexture::~ImplOpenGLTexture()
171 VCL_GL_INFO( "~OpenGLTexture " << mnTexture
);
174 // During shutdown GL is already de-initialized, so we should not try to create a new context.
176 rtl::Reference
<OpenGLContext
> xContext
= OpenGLContext::getVCLContext(false);
179 // FIXME: this is really not optimal performance-wise.
181 // Check we have been correctly un-bound from all framebuffers.
182 ImplSVData
* pSVData
= ImplGetSVData();
183 rtl::Reference
<OpenGLContext
> pContext
= pSVData
->maGDIData
.mpLastContext
;
187 pContext
->makeCurrent();
188 pContext
->UnbindTextureFromFramebuffers( mnTexture
);
191 if( mnOptStencil
!= 0 )
193 glDeleteRenderbuffers( 1, &mnOptStencil
);
196 auto& rState
= pContext
->state();
197 rState
.texture().unbindAndDelete(mnTexture
);
208 bool ImplOpenGLTexture::InsertBuffer(int nX
, int nY
, int nWidth
, int nHeight
, int nFormat
, int nType
, sal_uInt8
const * pData
)
210 if (!pData
|| mnTexture
== 0)
213 rtl::Reference
<OpenGLContext
> xContext
= OpenGLContext::getVCLContext();
214 xContext
->state().texture().active(0);
215 xContext
->state().texture().bind(mnTexture
);
217 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
219 glTexSubImage2D(GL_TEXTURE_2D
, 0, nX
, mnHeight
- nY
- nHeight
, nWidth
, nHeight
, nFormat
, nType
, pData
);
222 VCL_GL_INFO( "OpenGLTexture " << mnTexture
<< " Insert buff. to " << nX
<< " " << nY
223 << " size " << nWidth
<< "x" << nHeight
<< " from data" );
228 void ImplOpenGLTexture::InitializeSlotMechanism(int nInitialSlotSize
)
230 if (mpSlotReferences
)
233 mpSlotReferences
.reset(new std::vector
<int>(nInitialSlotSize
, 0));
236 void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber
)
238 if (mpSlotReferences
&& nSlotNumber
>= 0)
240 if (nSlotNumber
>= int(mpSlotReferences
->size()))
241 mpSlotReferences
->resize(nSlotNumber
+ 1, 0);
243 mpSlotReferences
->at(nSlotNumber
)++;
247 void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber
)
249 if (mpSlotReferences
&& nSlotNumber
>= 0)
251 if (nSlotNumber
>= int(mpSlotReferences
->size()))
252 mpSlotReferences
->resize(nSlotNumber
, 0);
254 mpSlotReferences
->at(nSlotNumber
)--;
256 if (mpSlotReferences
->at(nSlotNumber
) == 0 && mFunctSlotDeallocateCallback
)
258 mFunctSlotDeallocateCallback(nSlotNumber
);
263 OpenGLTexture::OpenGLTexture() :
264 maRect( 0, 0, 0, 0 ),
270 OpenGLTexture::OpenGLTexture(const std::shared_ptr
<ImplOpenGLTexture
>& rpImpl
, tools::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
) )
281 , 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
) )
288 , 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
) )
295 , mpImpl(new ImplOpenGLTexture(nWidth
, nHeight
, nFormat
, nType
, pData
))
301 OpenGLTexture::OpenGLTexture(const OpenGLTexture
& rTexture
)
302 : maRect(rTexture
.maRect
)
303 , mpImpl(rTexture
.mpImpl
)
304 , mnSlotNumber(rTexture
.mnSlotNumber
)
307 mpImpl
->IncreaseRefCount(mnSlotNumber
);
310 OpenGLTexture::OpenGLTexture(OpenGLTexture
&& rTexture
) noexcept
311 : maRect(rTexture
.maRect
)
312 , mpImpl(std::move(rTexture
.mpImpl
))
313 , mnSlotNumber(rTexture
.mnSlotNumber
)
317 OpenGLTexture::OpenGLTexture( const OpenGLTexture
& rTexture
,
318 int nX
, int nY
, int nWidth
, int nHeight
)
320 maRect
= tools::Rectangle( Point( rTexture
.maRect
.Left() + nX
, rTexture
.maRect
.Top() + nY
),
321 Size( nWidth
, nHeight
) );
322 mpImpl
= rTexture
.mpImpl
;
323 mnSlotNumber
= rTexture
.mnSlotNumber
;
325 mpImpl
->IncreaseRefCount(mnSlotNumber
);
326 VCL_GL_INFO( "Copying texture " << Id() << " [" << maRect
.Left() << "," << maRect
.Top() << "] " << GetWidth() << "x" << GetHeight() );
329 OpenGLTexture::~OpenGLTexture()
332 mpImpl
->DecreaseRefCount(mnSlotNumber
);
335 bool OpenGLTexture::IsUnique() const
337 return !mpImpl
|| (mpImpl
.use_count() == 1);
340 GLuint
OpenGLTexture::Id() const
343 return mpImpl
->mnTexture
;
347 int OpenGLTexture::GetWidth() const
349 return maRect
.GetWidth();
352 int OpenGLTexture::GetHeight() const
354 return maRect
.GetHeight();
357 GLuint
OpenGLTexture::StencilId() const
359 return mpImpl
? mpImpl
->mnOptStencil
: 0;
362 GLuint
OpenGLTexture::AddStencil()
365 return mpImpl
->AddStencil();
370 void OpenGLTexture::GetCoord( GLfloat
* pCoord
, const SalTwoRect
& rPosAry
, bool bInverted
) const
372 VCL_GL_INFO( "Getting coord " << Id() << " [" << maRect
.Left() << "," << maRect
.Top() << "] " << GetWidth() << "x" << GetHeight() );
376 pCoord
[0] = pCoord
[1] = pCoord
[2] = pCoord
[3] = 0.0f
;
377 pCoord
[4] = pCoord
[5] = pCoord
[6] = pCoord
[7] = 0.0f
;
381 pCoord
[0] = pCoord
[2] = (maRect
.Left() + rPosAry
.mnSrcX
) / static_cast<double>(mpImpl
->mnWidth
);
382 pCoord
[4] = pCoord
[6] = (maRect
.Left() + rPosAry
.mnSrcX
+ rPosAry
.mnSrcWidth
) / static_cast<double>(mpImpl
->mnWidth
);
386 pCoord
[3] = pCoord
[5] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
) / static_cast<double>(mpImpl
->mnHeight
);
387 pCoord
[1] = pCoord
[7] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
+ rPosAry
.mnSrcHeight
) / static_cast<double>(mpImpl
->mnHeight
);
391 pCoord
[1] = pCoord
[7] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
) / static_cast<double>(mpImpl
->mnHeight
);
392 pCoord
[3] = pCoord
[5] = 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
+ rPosAry
.mnSrcHeight
) / static_cast<double>(mpImpl
->mnHeight
);
396 void OpenGLTexture::GetTextureRect(const SalTwoRect
& rPosAry
, GLfloat
& x1
, GLfloat
& x2
, GLfloat
& y1
, GLfloat
& y2
) const
400 double fTextureWidth(mpImpl
->mnWidth
);
401 double fTextureHeight(mpImpl
->mnHeight
);
403 x1
= (maRect
.Left() + rPosAry
.mnSrcX
) / fTextureWidth
;
404 x2
= (maRect
.Left() + rPosAry
.mnSrcX
+ rPosAry
.mnSrcWidth
) / fTextureWidth
;
406 y1
= 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
) / fTextureHeight
;
407 y2
= 1.0f
- (maRect
.Top() + rPosAry
.mnSrcY
+ rPosAry
.mnSrcHeight
) / fTextureHeight
;
412 void OpenGLTexture::FillCoords
<GL_TRIANGLE_FAN
>(std::vector
<GLfloat
>& rCoords
, const SalTwoRect
& rPosAry
) const
419 GetTextureRect(rPosAry
, x1
, x2
, y1
, y2
);
421 rCoords
.insert(rCoords
.end(), {
428 void OpenGLTexture::FillCoords
<GL_TRIANGLES
>(std::vector
<GLfloat
>& rCoords
, const SalTwoRect
& rPosAry
) const
435 GetTextureRect(rPosAry
, x1
, x2
, y1
, y2
);
437 rCoords
.insert(rCoords
.end(), {
438 x1
, y1
, x2
, y1
, x1
, y2
,
439 x1
, y2
, x2
, y1
, x2
, y2
443 void OpenGLTexture::GetWholeCoord( GLfloat
* pCoord
) const
445 if( GetWidth() != mpImpl
->mnWidth
|| GetHeight() != mpImpl
->mnHeight
)
447 pCoord
[0] = pCoord
[2] = maRect
.Left() / static_cast<double>(mpImpl
->mnWidth
);
448 pCoord
[4] = pCoord
[6] = maRect
.Right() / static_cast<double>(mpImpl
->mnWidth
);
449 pCoord
[3] = pCoord
[5] = 1.0f
- maRect
.Top() / static_cast<double>(mpImpl
->mnHeight
);
450 pCoord
[1] = pCoord
[7] = 1.0f
- maRect
.Bottom() / static_cast<double>(mpImpl
->mnHeight
);
454 pCoord
[0] = pCoord
[2] = 0;
455 pCoord
[4] = pCoord
[6] = 1;
456 pCoord
[1] = pCoord
[7] = 0;
457 pCoord
[3] = pCoord
[5] = 1;
461 GLenum
OpenGLTexture::GetFilter() const
464 return mpImpl
->mnFilter
;
468 bool OpenGLTexture::CopyData(int nWidth
, int nHeight
, int nFormat
, int nType
, sal_uInt8
const * pData
)
470 if (!pData
|| !IsValid())
473 int nX
= maRect
.Left();
474 int nY
= maRect
.Top();
476 return mpImpl
->InsertBuffer(nX
, nY
, nWidth
, nHeight
, nFormat
, nType
, pData
);
479 void OpenGLTexture::SetFilter( GLenum nFilter
)
483 mpImpl
->mnFilter
= nFilter
;
484 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, nFilter
);
486 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, nFilter
);
491 void OpenGLTexture::Bind()
495 OpenGLContext::getVCLContext()->state().texture().bind(mpImpl
->mnTexture
);
498 VCL_GL_INFO( "OpenGLTexture::Binding invalid texture" );
503 void OpenGLTexture::Unbind()
507 OpenGLContext::getVCLContext()->state().texture().unbind(mpImpl
->mnTexture
);
511 void OpenGLTexture::SaveToFile(const OUString
& rFileName
)
513 std::vector
<sal_uInt8
> aBuffer(GetWidth() * GetHeight() * 4);
514 Read(OpenGLHelper::OptimalBufferFormat(), GL_UNSIGNED_BYTE
, aBuffer
.data());
515 BitmapEx aBitmap
= OpenGLHelper::ConvertBufferToBitmapEx(aBuffer
.data(), GetWidth(), GetHeight());
518 vcl::PNGWriter
aWriter(aBitmap
);
519 SvFileStream
sOutput(rFileName
, StreamMode::WRITE
);
520 aWriter
.Write(sOutput
);
525 SAL_WARN("vcl.opengl", "Error writing png to " << rFileName
);
529 void OpenGLTexture::Read( GLenum nFormat
, GLenum nType
, sal_uInt8
* pData
)
533 SAL_WARN( "vcl.opengl", "Can't read invalid texture" );
537 OpenGLVCLContextZone aContextZone
;
539 VCL_GL_INFO( "Reading texture " << Id() << " " << GetWidth() << "x" << GetHeight() );
541 if( GetWidth() == mpImpl
->mnWidth
&& GetHeight() == mpImpl
->mnHeight
)
544 glPixelStorei( GL_PACK_ALIGNMENT
, 1 );
546 // XXX: Call not available with GLES 2.0
547 glGetTexImage( GL_TEXTURE_2D
, 0, nFormat
, nType
, pData
);
553 long nWidth
= maRect
.GetWidth();
554 long nHeight
= maRect
.GetHeight();
555 long nX
= maRect
.Left();
556 long nY
= mpImpl
->mnHeight
- maRect
.Top() - nHeight
;
558 // Retrieve current context
559 ImplSVData
* pSVData
= ImplGetSVData();
560 rtl::Reference
<OpenGLContext
> pContext
= pSVData
->maGDIData
.mpLastContext
;
561 OpenGLFramebuffer
* pFramebuffer
= pContext
->AcquireFramebuffer(*this);
562 glPixelStorei(GL_PACK_ALIGNMENT
, 1);
564 glReadPixels(nX
, nY
, nWidth
, nHeight
, nFormat
, nType
, pData
);
566 OpenGLContext::ReleaseFramebuffer(pFramebuffer
);
570 OpenGLTexture::operator bool() const
575 OpenGLTexture
& OpenGLTexture::operator=(const OpenGLTexture
& rTexture
)
577 OpenGLTexture
aTemp(rTexture
);
578 *this = std::move(aTemp
);
582 OpenGLTexture
& OpenGLTexture::operator=(OpenGLTexture
&& rTexture
)
585 mpImpl
->DecreaseRefCount(mnSlotNumber
);
587 maRect
= rTexture
.maRect
;
588 mpImpl
= std::move(rTexture
.mpImpl
);
589 mnSlotNumber
= rTexture
.mnSlotNumber
;
594 bool OpenGLTexture::operator==( const OpenGLTexture
& rTexture
) const
596 return (mpImpl
== rTexture
.mpImpl
597 && maRect
== rTexture
.maRect
598 && mnSlotNumber
== rTexture
.mnSlotNumber
);
601 bool OpenGLTexture::operator!=( const OpenGLTexture
& rTexture
) const
603 return !( *this == rTexture
);
606 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */