Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / opengl / texture.cxx
blob9a3584c4f9cf27012292ad6955415ddc877040de
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 <vcl/opengl/OpenGLContext.hxx>
22 #include <vcl/opengl/OpenGLHelper.hxx>
24 #include "svdata.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"
34 namespace
37 constexpr GLenum constInternalFormat = GL_RGBA8;
39 } // end anonymous namespace
41 // texture with allocated size
42 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate ) :
43 mnTexture( 0 ),
44 mnWidth( nWidth ),
45 mnHeight( nHeight ),
46 mnFilter( GL_NEAREST ),
47 mnOptStencil( 0 )
49 OpenGLVCLContextZone aContextZone;
51 auto& rState = OpenGLContext::getVCLContext()->state();
52 TextureState::generate(mnTexture);
53 rState.texture().active(0);
54 rState.texture().bind(mnTexture);
56 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
57 CHECK_GL_ERROR();
58 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
59 CHECK_GL_ERROR();
60 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
61 CHECK_GL_ERROR();
62 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
63 CHECK_GL_ERROR();
64 if( bAllocate )
66 glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
67 CHECK_GL_ERROR();
70 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " allocate" );
73 // texture with content retrieved from FBO
74 ImplOpenGLTexture::ImplOpenGLTexture( int nX, int nY, int nWidth, int nHeight ) :
75 mnTexture( 0 ),
76 mnWidth( nWidth ),
77 mnHeight( nHeight ),
78 mnFilter( GL_NEAREST ),
79 mnOptStencil( 0 )
81 OpenGLVCLContextZone aContextZone;
83 // FIXME We need the window height here
84 // nY = GetHeight() - nHeight - nY;
86 auto& rState = OpenGLContext::getVCLContext()->state();
87 TextureState::generate(mnTexture);
88 rState.texture().active(0);
89 rState.texture().bind(mnTexture);
91 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
92 CHECK_GL_ERROR();
93 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
94 CHECK_GL_ERROR();
95 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
96 CHECK_GL_ERROR();
97 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
98 CHECK_GL_ERROR();
99 glCopyTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nX, nY, nWidth, nHeight, 0 );
100 CHECK_GL_ERROR();
102 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " from x" << nX << ", y" << nY );
105 // texture from buffer data
106 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData ) :
107 mnTexture( 0 ),
108 mnWidth( nWidth ),
109 mnHeight( nHeight ),
110 mnFilter( GL_NEAREST ),
111 mnOptStencil( 0 )
113 OpenGLVCLContextZone aContextZone;
115 auto& rState = OpenGLContext::getVCLContext()->state();
116 TextureState::generate(mnTexture);
117 rState.texture().active(0);
118 rState.texture().bind(mnTexture);
120 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
121 CHECK_GL_ERROR();
122 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
123 CHECK_GL_ERROR();
124 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
125 CHECK_GL_ERROR();
126 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
127 CHECK_GL_ERROR();
128 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
129 CHECK_GL_ERROR();
130 glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, mnWidth, mnHeight, 0, nFormat, nType, pData );
131 CHECK_GL_ERROR();
133 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " from data" );
136 GLuint ImplOpenGLTexture::AddStencil()
138 assert( mnOptStencil == 0 );
140 glGenRenderbuffers( 1, &mnOptStencil );
141 CHECK_GL_ERROR();
142 glBindRenderbuffer( GL_RENDERBUFFER, mnOptStencil );
143 CHECK_GL_ERROR();
144 VCL_GL_INFO( "Allocate stencil " << mnWidth << " x " << mnHeight );
145 glRenderbufferStorage( GL_RENDERBUFFER, GL_STENCIL_INDEX,
146 mnWidth, mnHeight );
147 CHECK_GL_ERROR();
148 glBindRenderbuffer(GL_RENDERBUFFER, 0);
149 CHECK_GL_ERROR();
151 return mnOptStencil;
154 ImplOpenGLTexture::~ImplOpenGLTexture()
156 VCL_GL_INFO( "~OpenGLTexture " << mnTexture );
157 if( mnTexture != 0 )
159 // During shutdown GL is already de-initialized, so we should not try to create a new context.
160 OpenGLZone aZone;
161 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext(false);
162 if( xContext.is() )
164 // FIXME: this is really not optimal performance-wise.
166 // Check we have been correctly un-bound from all framebuffers.
167 ImplSVData* pSVData = ImplGetSVData();
168 rtl::Reference<OpenGLContext> pContext = pSVData->maGDIData.mpLastContext;
170 if( pContext.is() )
172 pContext->makeCurrent();
173 pContext->UnbindTextureFromFramebuffers( mnTexture );
176 if( mnOptStencil != 0 )
178 glDeleteRenderbuffers( 1, &mnOptStencil );
179 mnOptStencil = 0;
181 auto& rState = pContext->state();
182 rState.texture().unbindAndDelete(mnTexture);
183 mnTexture = 0;
185 else
187 mnOptStencil = 0;
188 mnTexture = 0;
193 bool ImplOpenGLTexture::InsertBuffer(int nX, int nY, int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
195 if (!pData || mnTexture == 0)
196 return false;
198 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
199 xContext->state().texture().active(0);
200 xContext->state().texture().bind(mnTexture);
202 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
203 CHECK_GL_ERROR();
204 glTexSubImage2D(GL_TEXTURE_2D, 0, nX, mnHeight - nY - nHeight, nWidth, nHeight, nFormat, nType, pData);
205 CHECK_GL_ERROR();
207 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " Insert buff. to " << nX << " " << nY
208 << " size " << nWidth << "x" << nHeight << " from data" );
210 return true;
213 bool ImplOpenGLTexture::InitializeSlotMechanism(int nInitialSlotSize)
215 if (mpSlotReferences)
216 return false;
218 mpSlotReferences.reset(new std::vector<int>(nInitialSlotSize, 0));
219 return true;
222 void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber)
224 if (mpSlotReferences && nSlotNumber >= 0)
226 if (nSlotNumber >= int(mpSlotReferences->size()))
227 mpSlotReferences->resize(nSlotNumber + 1, 0);
229 mpSlotReferences->at(nSlotNumber)++;
233 void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber)
235 if (mpSlotReferences && nSlotNumber >= 0)
237 if (nSlotNumber >= int(mpSlotReferences->size()))
238 mpSlotReferences->resize(nSlotNumber, 0);
240 mpSlotReferences->at(nSlotNumber)--;
242 if (mpSlotReferences->at(nSlotNumber) == 0 && mFunctSlotDeallocateCallback)
244 mFunctSlotDeallocateCallback(nSlotNumber);
249 OpenGLTexture::OpenGLTexture() :
250 maRect( 0, 0, 0, 0 ),
251 mpImpl(),
252 mnSlotNumber(-1)
256 OpenGLTexture::OpenGLTexture(const std::shared_ptr<ImplOpenGLTexture>& rpImpl, tools::Rectangle aRectangle, int nSlotNumber)
257 : maRect(aRectangle)
258 , mpImpl(rpImpl)
259 , mnSlotNumber(nSlotNumber)
261 if (mpImpl)
262 mpImpl->IncreaseRefCount(nSlotNumber);
265 OpenGLTexture::OpenGLTexture( int nWidth, int nHeight, bool bAllocate )
266 : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
267 , mpImpl(new ImplOpenGLTexture(nWidth, nHeight, bAllocate))
268 , mnSlotNumber(-1)
272 OpenGLTexture::OpenGLTexture( int nX, int nY, int nWidth, int nHeight )
273 : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
274 , mpImpl(new ImplOpenGLTexture(nX, nY, nWidth, nHeight))
275 , mnSlotNumber(-1)
279 OpenGLTexture::OpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData )
280 : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
281 , mpImpl(new ImplOpenGLTexture(nWidth, nHeight, nFormat, nType, pData))
282 , mnSlotNumber(-1)
287 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( const OpenGLTexture& rTexture,
298 int nX, int nY, int nWidth, int nHeight )
300 maRect = tools::Rectangle( Point( rTexture.maRect.Left() + nX, rTexture.maRect.Top() + nY ),
301 Size( nWidth, nHeight ) );
302 mpImpl = rTexture.mpImpl;
303 mnSlotNumber = rTexture.mnSlotNumber;
304 if (mpImpl)
305 mpImpl->IncreaseRefCount(mnSlotNumber);
306 VCL_GL_INFO( "Copying texture " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
309 OpenGLTexture::~OpenGLTexture()
311 if (mpImpl)
312 mpImpl->DecreaseRefCount(mnSlotNumber);
315 bool OpenGLTexture::IsUnique() const
317 return !mpImpl || mpImpl.unique();
320 GLuint OpenGLTexture::Id() const
322 if (mpImpl)
323 return mpImpl->mnTexture;
324 return 0;
327 int OpenGLTexture::GetWidth() const
329 return maRect.GetWidth();
332 int OpenGLTexture::GetHeight() const
334 return maRect.GetHeight();
337 GLuint OpenGLTexture::StencilId() const
339 return mpImpl ? mpImpl->mnOptStencil : 0;
342 GLuint OpenGLTexture::AddStencil()
344 if (mpImpl)
345 return mpImpl->AddStencil();
346 else
347 return 0;
350 void OpenGLTexture::GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool bInverted ) const
352 VCL_GL_INFO( "Getting coord " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
354 if (!IsValid())
356 pCoord[0] = pCoord[1] = pCoord[2] = pCoord[3] = 0.0f;
357 pCoord[4] = pCoord[5] = pCoord[6] = pCoord[7] = 0.0f;
358 return;
361 pCoord[0] = pCoord[2] = (maRect.Left() + rPosAry.mnSrcX) / (double) mpImpl->mnWidth;
362 pCoord[4] = pCoord[6] = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / (double) mpImpl->mnWidth;
364 if( !bInverted )
366 pCoord[3] = pCoord[5] = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / (double) mpImpl->mnHeight;
367 pCoord[1] = pCoord[7] = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / (double) mpImpl->mnHeight;
369 else
371 pCoord[1] = pCoord[7] = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / (double) mpImpl->mnHeight;
372 pCoord[3] = pCoord[5] = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / (double) mpImpl->mnHeight;
376 bool OpenGLTexture::GetTextureRect(const SalTwoRect& rPosAry, bool bInverted, GLfloat& x1, GLfloat& x2, GLfloat& y1, GLfloat& y2) const
378 if (IsValid())
380 double fTextureWidth(mpImpl->mnWidth);
381 double fTextureHeight(mpImpl->mnHeight);
383 x1 = (maRect.Left() + rPosAry.mnSrcX) / fTextureWidth;
384 x2 = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / fTextureWidth;
386 if (bInverted)
388 y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
389 y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
391 else
393 y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
394 y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
396 return true;
398 return false;
401 template <>
402 void OpenGLTexture::FillCoords<GL_TRIANGLE_FAN>(std::vector<GLfloat>& rCoords, const SalTwoRect& rPosAry, bool bInverted) const
404 GLfloat x1 = 0.0f;
405 GLfloat x2 = 0.0f;
406 GLfloat y1 = 0.0f;
407 GLfloat y2 = 0.0f;
409 GetTextureRect(rPosAry, bInverted, x1, x2, y1, y2);
411 rCoords.insert(rCoords.end(), {
412 x1, y2, x1, y1,
413 x2, y1, x2, y2
417 template <>
418 void OpenGLTexture::FillCoords<GL_TRIANGLES>(std::vector<GLfloat>& rCoords, const SalTwoRect& rPosAry, bool bInverted) const
420 GLfloat x1 = 0.0f;
421 GLfloat x2 = 0.0f;
422 GLfloat y1 = 0.0f;
423 GLfloat y2 = 0.0f;
425 GetTextureRect(rPosAry, bInverted, x1, x2, y1, y2);
427 rCoords.insert(rCoords.end(), {
428 x1, y1, x2, y1, x1, y2,
429 x1, y2, x2, y1, x2, y2
433 void OpenGLTexture::GetWholeCoord( GLfloat* pCoord ) const
435 if( GetWidth() != mpImpl->mnWidth || GetHeight() != mpImpl->mnHeight )
437 pCoord[0] = pCoord[2] = maRect.Left() / (double) mpImpl->mnWidth;
438 pCoord[4] = pCoord[6] = maRect.Right() / (double) mpImpl->mnWidth;
439 pCoord[3] = pCoord[5] = 1.0f - maRect.Top() / (double) mpImpl->mnHeight;
440 pCoord[1] = pCoord[7] = 1.0f - maRect.Bottom() / (double) mpImpl->mnHeight;
442 else
444 pCoord[0] = pCoord[2] = 0;
445 pCoord[4] = pCoord[6] = 1;
446 pCoord[1] = pCoord[7] = 0;
447 pCoord[3] = pCoord[5] = 1;
451 GLenum OpenGLTexture::GetFilter() const
453 if( mpImpl )
454 return mpImpl->mnFilter;
455 return GL_NEAREST;
458 bool OpenGLTexture::CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
460 if (!pData || !IsValid())
461 return false;
463 int nX = maRect.Left();
464 int nY = maRect.Top();
466 return mpImpl->InsertBuffer(nX, nY, nWidth, nHeight, nFormat, nType, pData);
469 void OpenGLTexture::SetFilter( GLenum nFilter )
471 if( mpImpl )
473 mpImpl->mnFilter = nFilter;
474 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nFilter );
475 CHECK_GL_ERROR();
476 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nFilter );
477 CHECK_GL_ERROR();
481 void OpenGLTexture::Bind()
483 if (IsValid())
485 OpenGLContext::getVCLContext()->state().texture().bind(mpImpl->mnTexture);
487 else
488 VCL_GL_INFO( "OpenGLTexture::Binding invalid texture" );
490 CHECK_GL_ERROR();
493 void OpenGLTexture::Unbind()
495 if (IsValid())
497 OpenGLContext::getVCLContext()->state().texture().unbind(mpImpl->mnTexture);
501 void OpenGLTexture::SaveToFile(const OUString& rFileName)
503 std::vector<sal_uInt8> aBuffer(GetWidth() * GetHeight() * 4);
504 Read(GL_BGRA, GL_UNSIGNED_BYTE, aBuffer.data());
505 BitmapEx aBitmap = OpenGLHelper::ConvertBGRABufferToBitmapEx(aBuffer.data(), GetWidth(), GetHeight());
508 vcl::PNGWriter aWriter(aBitmap);
509 SvFileStream sOutput(rFileName, StreamMode::WRITE);
510 aWriter.Write(sOutput);
511 sOutput.Close();
513 catch (...)
515 SAL_WARN("vcl.opengl", "Error writing png to " << rFileName);
519 void OpenGLTexture::Read( GLenum nFormat, GLenum nType, sal_uInt8* pData )
521 if (!IsValid())
523 SAL_WARN( "vcl.opengl", "Can't read invalid texture" );
524 return;
527 OpenGLVCLContextZone aContextZone;
529 VCL_GL_INFO( "Reading texture " << Id() << " " << GetWidth() << "x" << GetHeight() );
531 if( GetWidth() == mpImpl->mnWidth && GetHeight() == mpImpl->mnHeight )
533 Bind();
534 glPixelStorei( GL_PACK_ALIGNMENT, 1 );
535 CHECK_GL_ERROR();
536 // XXX: Call not available with GLES 2.0
537 glGetTexImage( GL_TEXTURE_2D, 0, nFormat, nType, pData );
538 CHECK_GL_ERROR();
539 Unbind();
541 else
543 long nWidth = maRect.GetWidth();
544 long nHeight = maRect.GetHeight();
545 long nX = maRect.Left();
546 long nY = mpImpl->mnHeight - maRect.Top() - nHeight;
548 // Retrieve current context
549 ImplSVData* pSVData = ImplGetSVData();
550 rtl::Reference<OpenGLContext> pContext = pSVData->maGDIData.mpLastContext;
551 OpenGLFramebuffer* pFramebuffer = pContext->AcquireFramebuffer(*this);
552 glPixelStorei(GL_PACK_ALIGNMENT, 1);
553 CHECK_GL_ERROR();
554 glReadPixels(nX, nY, nWidth, nHeight, nFormat, nType, pData);
555 CHECK_GL_ERROR();
556 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
560 OpenGLTexture::operator bool() const
562 return IsValid();
565 OpenGLTexture& OpenGLTexture::operator=( const OpenGLTexture& rTexture )
567 if (rTexture.mpImpl)
569 rTexture.mpImpl->IncreaseRefCount(rTexture.mnSlotNumber);
572 if (mpImpl)
573 mpImpl->DecreaseRefCount(mnSlotNumber);
575 maRect = rTexture.maRect;
576 mpImpl = rTexture.mpImpl;
577 mnSlotNumber = rTexture.mnSlotNumber;
579 return *this;
582 bool OpenGLTexture::operator==( const OpenGLTexture& rTexture ) const
584 return (mpImpl == rTexture.mpImpl
585 && maRect == rTexture.maRect
586 && mnSlotNumber == rTexture.mnSlotNumber);
589 bool OpenGLTexture::operator!=( const OpenGLTexture& rTexture ) const
591 return !( *this == rTexture );
594 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */