Version 5.2.0.0.beta1, tag libreoffice-5.2.0.0.beta1
[LibreOffice.git] / vcl / opengl / texture.cxx
blob0999484e0a6055037c1d03155990cca4fdbc1da7
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 SAL_CONSTEXPR GLenum constInternalFormat = GL_RGBA8;
39 } // end anonymous namespace
41 // texture with allocated size
42 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate ) :
43 mnRefCount( 1 ),
44 mnTexture( 0 ),
45 mnWidth( nWidth ),
46 mnHeight( nHeight ),
47 mnFilter( GL_NEAREST ),
48 mnOptStencil( 0 )
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 );
58 CHECK_GL_ERROR();
59 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
60 CHECK_GL_ERROR();
61 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
62 CHECK_GL_ERROR();
63 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
64 CHECK_GL_ERROR();
65 if( bAllocate )
67 glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
68 CHECK_GL_ERROR();
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 ) :
76 mnRefCount( 1 ),
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 mnRefCount( 1 ),
110 mnTexture( 0 ),
111 mnWidth( nWidth ),
112 mnHeight( nHeight ),
113 mnFilter( GL_NEAREST ),
114 mnOptStencil( 0 )
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 );
124 CHECK_GL_ERROR();
125 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
126 CHECK_GL_ERROR();
127 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
128 CHECK_GL_ERROR();
129 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
130 CHECK_GL_ERROR();
131 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
132 CHECK_GL_ERROR();
133 glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, mnWidth, mnHeight, 0, nFormat, nType, pData );
134 CHECK_GL_ERROR();
136 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " from data" );
139 GLuint ImplOpenGLTexture::AddStencil()
141 assert( mnOptStencil == 0 );
143 glGenRenderbuffers( 1, &mnOptStencil );
144 CHECK_GL_ERROR();
145 glBindRenderbuffer( GL_RENDERBUFFER, mnOptStencil );
146 CHECK_GL_ERROR();
147 VCL_GL_INFO( "Allocate stencil " << mnWidth << " x " << mnHeight );
148 glRenderbufferStorage( GL_RENDERBUFFER, GL_STENCIL_INDEX,
149 mnWidth, mnHeight );
150 CHECK_GL_ERROR();
151 glBindRenderbuffer(GL_RENDERBUFFER, 0);
152 CHECK_GL_ERROR();
154 return mnOptStencil;
157 ImplOpenGLTexture::~ImplOpenGLTexture()
159 VCL_GL_INFO( "~OpenGLTexture " << mnTexture );
160 Dispose();
163 void ImplOpenGLTexture::Dispose()
165 if( mnTexture != 0 )
167 // During shutdown GL is already de-initialized, so we should not try to create a new context.
168 OpenGLZone aZone;
169 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext(false);
170 if( xContext.is() )
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;
178 if( pContext.is() )
180 pContext->makeCurrent();
181 pContext->UnbindTextureFromFramebuffers( mnTexture );
184 if( mnOptStencil != 0 )
186 glDeleteRenderbuffers( 1, &mnOptStencil );
187 mnOptStencil = 0;
189 auto& rState = pContext->state();
190 rState->texture().unbindAndDelete(mnTexture);
191 mnTexture = 0;
193 else
195 mnOptStencil = 0;
196 mnTexture = 0;
201 bool ImplOpenGLTexture::InsertBuffer(int nX, int nY, int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
203 if (!pData || mnTexture == 0)
204 return false;
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);
211 CHECK_GL_ERROR();
212 glTexSubImage2D(GL_TEXTURE_2D, 0, nX, mnHeight - nY - nHeight, nWidth, nHeight, nFormat, nType, pData);
213 CHECK_GL_ERROR();
215 VCL_GL_INFO( "OpenGLTexture " << mnTexture << " Insert buff. to " << nX << " " << nY
216 << " size " << nWidth << "x" << nHeight << " from data" );
218 return true;
221 bool ImplOpenGLTexture::InitializeSlotMechanism(int nInitialSlotSize)
223 if (mpSlotReferences)
224 return false;
226 mpSlotReferences.reset(new std::vector<int>(nInitialSlotSize, 0));
227 return true;
230 void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber)
232 mnRefCount++;
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);
257 mnRefCount--;
258 if (mnRefCount <= 0)
259 delete this;
263 OpenGLTexture::OpenGLTexture() :
264 maRect( 0, 0, 0, 0 ),
265 mpImpl(nullptr),
266 mnSlotNumber(-1)
270 OpenGLTexture::OpenGLTexture(ImplOpenGLTexture* pImpl, Rectangle aRectangle, int nSlotNumber)
271 : maRect(aRectangle)
272 , mpImpl(pImpl)
273 , mnSlotNumber(nSlotNumber)
275 if (mpImpl)
276 mpImpl->IncreaseRefCount(nSlotNumber);
279 OpenGLTexture::OpenGLTexture( int nWidth, int nHeight, bool bAllocate )
280 : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
281 , mnSlotNumber(-1)
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 ) )
288 , mnSlotNumber(-1)
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 ) )
295 , mnSlotNumber(-1)
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;
306 if (mpImpl)
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;
317 if (mpImpl)
318 mpImpl->IncreaseRefCount(mnSlotNumber);
319 VCL_GL_INFO( "Copying texture " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
322 OpenGLTexture::~OpenGLTexture()
324 if (mpImpl)
325 mpImpl->DecreaseRefCount(mnSlotNumber);
328 bool OpenGLTexture::IsUnique() const
330 return mpImpl == nullptr || mpImpl->IsUnique();
333 GLuint OpenGLTexture::Id() const
335 if( mpImpl )
336 return mpImpl->mnTexture;
337 return 0;
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()
357 if (mpImpl)
358 return mpImpl->AddStencil();
359 else
360 return 0;
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;
371 return;
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;
377 if( !bInverted )
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;
382 else
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;
389 template <>
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 << ")");
396 GLfloat x1 = 0.0f;
397 GLfloat x2 = 0.0f;
398 GLfloat y1 = 0.0f;
399 GLfloat y2 = 0.0f;
401 if (mpImpl)
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;
409 if (bInverted)
411 y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
412 y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
414 else
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;
449 else
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()
460 if (mpImpl)
461 return OpenGLTexture(mpImpl, Rectangle(Point(0, 0), Size(mpImpl->mnWidth, mpImpl->mnHeight)), -1);
462 return OpenGLTexture();
465 GLenum OpenGLTexture::GetFilter() const
467 if( mpImpl )
468 return mpImpl->mnFilter;
469 return GL_NEAREST;
472 bool OpenGLTexture::CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
474 if (!pData || mpImpl == nullptr)
475 return false;
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 )
485 if( mpImpl )
487 mpImpl->mnFilter = nFilter;
488 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nFilter );
489 CHECK_GL_ERROR();
490 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nFilter );
491 CHECK_GL_ERROR();
495 void OpenGLTexture::Bind()
497 if (mpImpl)
499 std::unique_ptr<RenderState>& rState = OpenGLContext::getVCLContext()->state();
500 rState->texture().bind(mpImpl->mnTexture);
502 else
503 VCL_GL_INFO( "OpenGLTexture::Binding invalid texture" );
505 CHECK_GL_ERROR();
508 void OpenGLTexture::Unbind()
510 if (mpImpl)
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);
527 sOutput.Close();
529 catch (...)
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" );
540 return;
543 OpenGLVCLContextZone aContextZone;
545 VCL_GL_INFO( "Reading texture " << Id() << " " << GetWidth() << "x" << GetHeight() );
547 if( GetWidth() == mpImpl->mnWidth && GetHeight() == mpImpl->mnHeight )
549 Bind();
550 glPixelStorei( GL_PACK_ALIGNMENT, 1 );
551 CHECK_GL_ERROR();
552 // XXX: Call not available with GLES 2.0
553 glGetTexImage( GL_TEXTURE_2D, 0, nFormat, nType, pData );
554 CHECK_GL_ERROR();
555 Unbind();
557 else
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);
569 CHECK_GL_ERROR();
570 glReadPixels(nX, nY, nWidth, nHeight, nFormat, nType, pData);
571 CHECK_GL_ERROR();
572 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
576 OpenGLTexture::operator bool() const
578 return ( mpImpl != nullptr );
581 OpenGLTexture& OpenGLTexture::operator=( const OpenGLTexture& rTexture )
583 if (rTexture.mpImpl)
585 rTexture.mpImpl->IncreaseRefCount(rTexture.mnSlotNumber);
588 if (mpImpl)
589 mpImpl->DecreaseRefCount(mnSlotNumber);
591 maRect = rTexture.maRect;
592 mpImpl = rTexture.mpImpl;
593 mnSlotNumber = rTexture.mnSlotNumber;
595 return *this;
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: */