sd a11y: fix dozen of missing mnemonics
[LibreOffice.git] / vcl / opengl / texture.cxx
blob2862cce52352cf0be485567054e76ca306be7c87
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
26 #include <svdata.hxx>
28 #include <vcl/salbtype.hxx>
29 #include <vcl/pngwrite.hxx>
31 #include <opengl/framebuffer.hxx>
32 #include <opengl/texture.hxx>
33 #include <opengl/zone.hxx>
34 #include <opengl/RenderState.hxx>
36 namespace
39 constexpr GLenum constInternalFormat = GL_RGBA8;
41 } // end anonymous namespace
43 // texture with allocated size
44 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate ) :
45 mnTexture( 0 ),
46 mnWidth( nWidth ),
47 mnHeight( nHeight ),
48 mnFilter( GL_NEAREST ),
49 mnOptStencil( 0 )
51 OpenGLVCLContextZone aContextZone;
53 auto& rState = OpenGLContext::getVCLContext()->state();
54 TextureState::generate(mnTexture);
55 rState.texture().active(0);
56 rState.texture().bind(mnTexture);
58 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
59 CHECK_GL_ERROR();
60 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
61 CHECK_GL_ERROR();
62 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
63 CHECK_GL_ERROR();
64 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
65 CHECK_GL_ERROR();
66 if( bAllocate )
68 glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
69 CHECK_GL_ERROR();
72 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " allocate" );
75 // texture with content retrieved from FBO
76 ImplOpenGLTexture::ImplOpenGLTexture( int nX, int nY, int nWidth, int nHeight ) :
77 mnTexture( 0 ),
78 mnWidth( nWidth ),
79 mnHeight( nHeight ),
80 mnFilter( GL_NEAREST ),
81 mnOptStencil( 0 )
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 );
94 CHECK_GL_ERROR();
95 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
96 CHECK_GL_ERROR();
97 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
98 CHECK_GL_ERROR();
99 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
100 CHECK_GL_ERROR();
101 glCopyTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nX, nY, nWidth, nHeight, 0 );
102 CHECK_GL_ERROR();
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 ) :
109 mnTexture( 0 ),
110 mnWidth( nWidth ),
111 mnHeight( nHeight ),
112 mnFilter( GL_NEAREST ),
113 mnOptStencil( 0 )
115 OpenGLVCLContextZone aContextZone;
117 auto& rState = OpenGLContext::getVCLContext()->state();
118 TextureState::generate(mnTexture);
119 rState.texture().active(0);
120 rState.texture().bind(mnTexture);
122 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
123 CHECK_GL_ERROR();
124 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
125 CHECK_GL_ERROR();
126 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
127 CHECK_GL_ERROR();
128 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
129 CHECK_GL_ERROR();
130 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
131 CHECK_GL_ERROR();
132 glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, mnWidth, mnHeight, 0, nFormat, nType, pData );
133 CHECK_GL_ERROR();
135 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " from data" );
138 GLuint ImplOpenGLTexture::AddStencil()
140 assert( mnOptStencil == 0 );
142 glGenRenderbuffers( 1, &mnOptStencil );
143 CHECK_GL_ERROR();
144 glBindRenderbuffer( GL_RENDERBUFFER, mnOptStencil );
145 CHECK_GL_ERROR();
146 VCL_GL_INFO( "Allocate stencil " << mnWidth << " x " << mnHeight );
147 glRenderbufferStorage( GL_RENDERBUFFER, GL_STENCIL_INDEX,
148 mnWidth, mnHeight );
149 CHECK_GL_ERROR();
150 glBindRenderbuffer(GL_RENDERBUFFER, 0);
151 CHECK_GL_ERROR();
153 return mnOptStencil;
156 ImplOpenGLTexture::~ImplOpenGLTexture()
158 VCL_GL_INFO( "~OpenGLTexture " << mnTexture );
159 if( mnTexture != 0 )
161 // During shutdown GL is already de-initialized, so we should not try to create a new context.
162 OpenGLZone aZone;
163 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext(false);
164 if( xContext.is() )
166 // FIXME: this is really not optimal performance-wise.
168 // Check we have been correctly un-bound from all framebuffers.
169 ImplSVData* pSVData = ImplGetSVData();
170 rtl::Reference<OpenGLContext> pContext = pSVData->maGDIData.mpLastContext;
172 if( pContext.is() )
174 pContext->makeCurrent();
175 pContext->UnbindTextureFromFramebuffers( mnTexture );
178 if( mnOptStencil != 0 )
180 glDeleteRenderbuffers( 1, &mnOptStencil );
181 mnOptStencil = 0;
183 auto& rState = pContext->state();
184 rState.texture().unbindAndDelete(mnTexture);
185 mnTexture = 0;
187 else
189 mnOptStencil = 0;
190 mnTexture = 0;
195 bool ImplOpenGLTexture::InsertBuffer(int nX, int nY, int nWidth, int nHeight, int nFormat, int nType, sal_uInt8 const * pData)
197 if (!pData || mnTexture == 0)
198 return false;
200 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
201 xContext->state().texture().active(0);
202 xContext->state().texture().bind(mnTexture);
204 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
205 CHECK_GL_ERROR();
206 glTexSubImage2D(GL_TEXTURE_2D, 0, nX, mnHeight - nY - nHeight, nWidth, nHeight, nFormat, nType, pData);
207 CHECK_GL_ERROR();
209 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " Insert buff. to " << nX << " " << nY
210 << " size " << nWidth << "x" << nHeight << " from data" );
212 return true;
215 void ImplOpenGLTexture::InitializeSlotMechanism(int nInitialSlotSize)
217 if (mpSlotReferences)
218 return;
220 mpSlotReferences.reset(new std::vector<int>(nInitialSlotSize, 0));
223 void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber)
225 if (mpSlotReferences && nSlotNumber >= 0)
227 if (nSlotNumber >= int(mpSlotReferences->size()))
228 mpSlotReferences->resize(nSlotNumber + 1, 0);
230 mpSlotReferences->at(nSlotNumber)++;
234 void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber)
236 if (mpSlotReferences && nSlotNumber >= 0)
238 if (nSlotNumber >= int(mpSlotReferences->size()))
239 mpSlotReferences->resize(nSlotNumber, 0);
241 mpSlotReferences->at(nSlotNumber)--;
243 if (mpSlotReferences->at(nSlotNumber) == 0 && mFunctSlotDeallocateCallback)
245 mFunctSlotDeallocateCallback(nSlotNumber);
250 OpenGLTexture::OpenGLTexture() :
251 maRect( 0, 0, 0, 0 ),
252 mpImpl(),
253 mnSlotNumber(-1)
257 OpenGLTexture::OpenGLTexture(const std::shared_ptr<ImplOpenGLTexture>& rpImpl, tools::Rectangle aRectangle, int nSlotNumber)
258 : maRect(aRectangle)
259 , mpImpl(rpImpl)
260 , mnSlotNumber(nSlotNumber)
262 if (mpImpl)
263 mpImpl->IncreaseRefCount(nSlotNumber);
266 OpenGLTexture::OpenGLTexture( int nWidth, int nHeight, bool bAllocate )
267 : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
268 , mpImpl(new ImplOpenGLTexture(nWidth, nHeight, bAllocate))
269 , mnSlotNumber(-1)
273 OpenGLTexture::OpenGLTexture( int nX, int nY, int nWidth, int nHeight )
274 : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
275 , mpImpl(new ImplOpenGLTexture(nX, nY, nWidth, nHeight))
276 , mnSlotNumber(-1)
280 OpenGLTexture::OpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData )
281 : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
282 , mpImpl(new ImplOpenGLTexture(nWidth, nHeight, nFormat, nType, pData))
283 , mnSlotNumber(-1)
288 OpenGLTexture::OpenGLTexture(const OpenGLTexture& rTexture)
289 : maRect(rTexture.maRect)
290 , mpImpl(rTexture.mpImpl)
291 , mnSlotNumber(rTexture.mnSlotNumber)
293 if (mpImpl)
294 mpImpl->IncreaseRefCount(mnSlotNumber);
297 OpenGLTexture::OpenGLTexture(OpenGLTexture&& rTexture)
298 : maRect(rTexture.maRect)
299 , mpImpl(std::move(rTexture.mpImpl))
300 , mnSlotNumber(rTexture.mnSlotNumber)
304 OpenGLTexture::OpenGLTexture( const OpenGLTexture& rTexture,
305 int nX, int nY, int nWidth, int nHeight )
307 maRect = tools::Rectangle( Point( rTexture.maRect.Left() + nX, rTexture.maRect.Top() + nY ),
308 Size( nWidth, nHeight ) );
309 mpImpl = rTexture.mpImpl;
310 mnSlotNumber = rTexture.mnSlotNumber;
311 if (mpImpl)
312 mpImpl->IncreaseRefCount(mnSlotNumber);
313 VCL_GL_INFO( "Copying texture " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
316 OpenGLTexture::~OpenGLTexture()
318 if (mpImpl)
319 mpImpl->DecreaseRefCount(mnSlotNumber);
322 bool OpenGLTexture::IsUnique() const
324 return !mpImpl || (mpImpl.use_count() == 1);
327 GLuint OpenGLTexture::Id() const
329 if (mpImpl)
330 return mpImpl->mnTexture;
331 return 0;
334 int OpenGLTexture::GetWidth() const
336 return maRect.GetWidth();
339 int OpenGLTexture::GetHeight() const
341 return maRect.GetHeight();
344 GLuint OpenGLTexture::StencilId() const
346 return mpImpl ? mpImpl->mnOptStencil : 0;
349 GLuint OpenGLTexture::AddStencil()
351 if (mpImpl)
352 return mpImpl->AddStencil();
353 else
354 return 0;
357 void OpenGLTexture::GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool bInverted ) const
359 VCL_GL_INFO( "Getting coord " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
361 if (!IsValid())
363 pCoord[0] = pCoord[1] = pCoord[2] = pCoord[3] = 0.0f;
364 pCoord[4] = pCoord[5] = pCoord[6] = pCoord[7] = 0.0f;
365 return;
368 pCoord[0] = pCoord[2] = (maRect.Left() + rPosAry.mnSrcX) / static_cast<double>(mpImpl->mnWidth);
369 pCoord[4] = pCoord[6] = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / static_cast<double>(mpImpl->mnWidth);
371 if( !bInverted )
373 pCoord[3] = pCoord[5] = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / static_cast<double>(mpImpl->mnHeight);
374 pCoord[1] = pCoord[7] = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / static_cast<double>(mpImpl->mnHeight);
376 else
378 pCoord[1] = pCoord[7] = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / static_cast<double>(mpImpl->mnHeight);
379 pCoord[3] = pCoord[5] = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / static_cast<double>(mpImpl->mnHeight);
383 void OpenGLTexture::GetTextureRect(const SalTwoRect& rPosAry, GLfloat& x1, GLfloat& x2, GLfloat& y1, GLfloat& y2) const
385 if (IsValid())
387 double fTextureWidth(mpImpl->mnWidth);
388 double fTextureHeight(mpImpl->mnHeight);
390 x1 = (maRect.Left() + rPosAry.mnSrcX) / fTextureWidth;
391 x2 = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / fTextureWidth;
393 y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
394 y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
398 template <>
399 void OpenGLTexture::FillCoords<GL_TRIANGLE_FAN>(std::vector<GLfloat>& rCoords, const SalTwoRect& rPosAry) const
401 GLfloat x1 = 0.0f;
402 GLfloat x2 = 0.0f;
403 GLfloat y1 = 0.0f;
404 GLfloat y2 = 0.0f;
406 GetTextureRect(rPosAry, x1, x2, y1, y2);
408 rCoords.insert(rCoords.end(), {
409 x1, y2, x1, y1,
410 x2, y1, x2, y2
414 template <>
415 void OpenGLTexture::FillCoords<GL_TRIANGLES>(std::vector<GLfloat>& rCoords, const SalTwoRect& rPosAry) const
417 GLfloat x1 = 0.0f;
418 GLfloat x2 = 0.0f;
419 GLfloat y1 = 0.0f;
420 GLfloat y2 = 0.0f;
422 GetTextureRect(rPosAry, x1, x2, y1, y2);
424 rCoords.insert(rCoords.end(), {
425 x1, y1, x2, y1, x1, y2,
426 x1, y2, x2, y1, x2, y2
430 void OpenGLTexture::GetWholeCoord( GLfloat* pCoord ) const
432 if( GetWidth() != mpImpl->mnWidth || GetHeight() != mpImpl->mnHeight )
434 pCoord[0] = pCoord[2] = maRect.Left() / static_cast<double>(mpImpl->mnWidth);
435 pCoord[4] = pCoord[6] = maRect.Right() / static_cast<double>(mpImpl->mnWidth);
436 pCoord[3] = pCoord[5] = 1.0f - maRect.Top() / static_cast<double>(mpImpl->mnHeight);
437 pCoord[1] = pCoord[7] = 1.0f - maRect.Bottom() / static_cast<double>(mpImpl->mnHeight);
439 else
441 pCoord[0] = pCoord[2] = 0;
442 pCoord[4] = pCoord[6] = 1;
443 pCoord[1] = pCoord[7] = 0;
444 pCoord[3] = pCoord[5] = 1;
448 GLenum OpenGLTexture::GetFilter() const
450 if( mpImpl )
451 return mpImpl->mnFilter;
452 return GL_NEAREST;
455 bool OpenGLTexture::CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8 const * pData)
457 if (!pData || !IsValid())
458 return false;
460 int nX = maRect.Left();
461 int nY = maRect.Top();
463 return mpImpl->InsertBuffer(nX, nY, nWidth, nHeight, nFormat, nType, pData);
466 void OpenGLTexture::SetFilter( GLenum nFilter )
468 if( mpImpl )
470 mpImpl->mnFilter = nFilter;
471 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nFilter );
472 CHECK_GL_ERROR();
473 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nFilter );
474 CHECK_GL_ERROR();
478 void OpenGLTexture::Bind()
480 if (IsValid())
482 OpenGLContext::getVCLContext()->state().texture().bind(mpImpl->mnTexture);
484 else
485 VCL_GL_INFO( "OpenGLTexture::Binding invalid texture" );
487 CHECK_GL_ERROR();
490 void OpenGLTexture::Unbind()
492 if (IsValid())
494 OpenGLContext::getVCLContext()->state().texture().unbind(mpImpl->mnTexture);
498 void OpenGLTexture::SaveToFile(const OUString& rFileName)
500 std::vector<sal_uInt8> aBuffer(GetWidth() * GetHeight() * 4);
501 Read(GL_BGRA, GL_UNSIGNED_BYTE, aBuffer.data());
502 BitmapEx aBitmap = OpenGLHelper::ConvertBGRABufferToBitmapEx(aBuffer.data(), GetWidth(), GetHeight());
505 vcl::PNGWriter aWriter(aBitmap);
506 SvFileStream sOutput(rFileName, StreamMode::WRITE);
507 aWriter.Write(sOutput);
508 sOutput.Close();
510 catch (...)
512 SAL_WARN("vcl.opengl", "Error writing png to " << rFileName);
516 void OpenGLTexture::Read( GLenum nFormat, GLenum nType, sal_uInt8* pData )
518 if (!IsValid())
520 SAL_WARN( "vcl.opengl", "Can't read invalid texture" );
521 return;
524 OpenGLVCLContextZone aContextZone;
526 VCL_GL_INFO( "Reading texture " << Id() << " " << GetWidth() << "x" << GetHeight() );
528 if( GetWidth() == mpImpl->mnWidth && GetHeight() == mpImpl->mnHeight )
530 Bind();
531 glPixelStorei( GL_PACK_ALIGNMENT, 1 );
532 CHECK_GL_ERROR();
533 // XXX: Call not available with GLES 2.0
534 glGetTexImage( GL_TEXTURE_2D, 0, nFormat, nType, pData );
535 CHECK_GL_ERROR();
536 Unbind();
538 else
540 long nWidth = maRect.GetWidth();
541 long nHeight = maRect.GetHeight();
542 long nX = maRect.Left();
543 long nY = mpImpl->mnHeight - maRect.Top() - nHeight;
545 // Retrieve current context
546 ImplSVData* pSVData = ImplGetSVData();
547 rtl::Reference<OpenGLContext> pContext = pSVData->maGDIData.mpLastContext;
548 OpenGLFramebuffer* pFramebuffer = pContext->AcquireFramebuffer(*this);
549 glPixelStorei(GL_PACK_ALIGNMENT, 1);
550 CHECK_GL_ERROR();
551 glReadPixels(nX, nY, nWidth, nHeight, nFormat, nType, pData);
552 CHECK_GL_ERROR();
553 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
557 OpenGLTexture::operator bool() const
559 return IsValid();
562 OpenGLTexture& OpenGLTexture::operator=(const OpenGLTexture& rTexture)
564 if (rTexture.mpImpl)
565 rTexture.mpImpl->IncreaseRefCount(rTexture.mnSlotNumber);
567 if (mpImpl)
568 mpImpl->DecreaseRefCount(mnSlotNumber);
570 maRect = rTexture.maRect;
571 mpImpl = rTexture.mpImpl;
572 mnSlotNumber = rTexture.mnSlotNumber;
574 return *this;
577 OpenGLTexture& OpenGLTexture::operator=(OpenGLTexture&& rTexture)
579 if (mpImpl)
580 mpImpl->DecreaseRefCount(mnSlotNumber);
582 maRect = rTexture.maRect;
583 mpImpl = std::move(rTexture.mpImpl);
584 mnSlotNumber = rTexture.mnSlotNumber;
586 return *this;
589 bool OpenGLTexture::operator==( const OpenGLTexture& rTexture ) const
591 return (mpImpl == rTexture.mpImpl
592 && maRect == rTexture.maRect
593 && mnSlotNumber == rTexture.mnSlotNumber);
596 bool OpenGLTexture::operator!=( const OpenGLTexture& rTexture ) const
598 return !( *this == rTexture );
601 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */