1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  */
9 
10 #include <opengl/program.hxx>
11 #include <opengl/RenderState.hxx>
12 
13 #include <vcl/opengl/OpenGLHelper.hxx>
14 #include <vcl/opengl/OpenGLContext.hxx>
15 
16 #include <glm/glm.hpp>
17 #include <glm/gtc/type_ptr.hpp>
18 #include <glm/gtc/matrix_transform.hpp>
19 
OpenGLProgram()20 OpenGLProgram::OpenGLProgram() :
21     mnId( 0 ),
22     mnEnabledAttribs( 0 ),
23     mnPositionAttrib( SAL_MAX_UINT32 ),
24     mnTexCoordAttrib( SAL_MAX_UINT32 ),
25     mnAlphaCoordAttrib( SAL_MAX_UINT32 ),
26     mnMaskCoordAttrib( SAL_MAX_UINT32 ),
27     mnExtrusionVectorsAttrib( SAL_MAX_UINT32 ),
28     mnVertexColorsAttrib( SAL_MAX_UINT32 ),
29     mbBlending(false),
30     mfLastWidth(0.0),
31     mfLastHeight(0.0),
32     mfLastPixelOffset(0.0)
33 {
34 }
35 
~OpenGLProgram()36 OpenGLProgram::~OpenGLProgram()
37 {
38     maUniformLocations.clear();
39     if( mnId != 0 )
40     {
41         glDeleteProgram( mnId );
42         CHECK_GL_ERROR();
43     }
44 }
45 
Load(const OUString & rVertexShader,const OUString & rFragmentShader,const OString & preamble,const OString & rDigest)46 bool OpenGLProgram::Load( const OUString& rVertexShader,
47                           const OUString& rFragmentShader,
48                           const OString& preamble,
49                           const OString& rDigest )
50 {
51     mnId = OpenGLHelper::LoadShaders( rVertexShader, rFragmentShader, preamble, rDigest );
52     return ( mnId != 0 );
53 }
54 
Reuse()55 void OpenGLProgram::Reuse()
56 {
57     mbBlending = false;
58 }
59 
Use()60 void OpenGLProgram::Use()
61 {
62     if (!mnId)
63         return;
64 
65     glUseProgram(mnId);
66     CHECK_GL_ERROR();
67     Reuse();
68 }
69 
Clean()70 void OpenGLProgram::Clean()
71 {
72     // unbind all textures
73     for (OpenGLTexture& rTexture : maTextures)
74     {
75         rTexture.Unbind();
76     }
77     maTextures.clear();
78 
79     // disable any enabled vertex attrib array
80     if( mnEnabledAttribs )
81     {
82         for( int i = 0; i < 32; i++ )
83         {
84             if( mnEnabledAttribs & ( 1 << i ) )
85             {
86                 glDisableVertexAttribArray( i );
87                 CHECK_GL_ERROR();
88             }
89         }
90         mnEnabledAttribs = 0;
91     }
92 }
93 
EnableVertexAttrib(GLuint & rAttrib,const OString & rName)94 bool OpenGLProgram::EnableVertexAttrib(GLuint& rAttrib, const OString& rName)
95 {
96     if( rAttrib == SAL_MAX_UINT32 )
97     {
98         GLint aLocation = glGetAttribLocation(mnId, rName.getStr());
99         CHECK_GL_ERROR();
100         if (aLocation < 0)
101             return false;
102         rAttrib = GLuint(aLocation);
103     }
104     if( (mnEnabledAttribs & ( 1 << rAttrib )) == 0 )
105     {
106         glEnableVertexAttribArray( rAttrib );
107         CHECK_GL_ERROR();
108         mnEnabledAttribs |= ( 1 << rAttrib );
109     }
110     return true;
111 }
112 
SetVertexAttrib(GLuint & rAttrib,const OString & rName,GLint nSize,GLenum eType,GLboolean bNormalized,GLsizei aStride,const GLvoid * pPointer)113 void OpenGLProgram::SetVertexAttrib(GLuint& rAttrib, const OString& rName, GLint nSize,
114                                     GLenum eType, GLboolean bNormalized, GLsizei aStride,
115                                     const GLvoid* pPointer)
116 {
117     if (EnableVertexAttrib(rAttrib, rName))
118     {
119         glVertexAttribPointer(rAttrib, nSize, eType, bNormalized, aStride, pPointer);
120         CHECK_GL_ERROR();
121     }
122     else
123     {
124         VCL_GL_INFO("Vertex attribute '" << rName << "' doesn't exist in this program (" << mnId << ")");
125     }
126 }
127 
SetVertices(const GLvoid * pData)128 void OpenGLProgram::SetVertices( const GLvoid* pData )
129 {
130     SetVertexAttrib(mnPositionAttrib, "position", 2, GL_FLOAT, GL_FALSE, 0, pData);
131 }
132 
SetTextureCoord(const GLvoid * pData)133 void OpenGLProgram::SetTextureCoord( const GLvoid* pData )
134 {
135     SetVertexAttrib(mnTexCoordAttrib, "tex_coord_in", 2, GL_FLOAT, GL_FALSE, 0, pData);
136 }
137 
SetAlphaCoord(const GLvoid * pData)138 void OpenGLProgram::SetAlphaCoord( const GLvoid* pData )
139 {
140     SetVertexAttrib(mnAlphaCoordAttrib, "alpha_coord_in", 2, GL_FLOAT, GL_FALSE, 0, pData);
141 }
142 
SetMaskCoord(const GLvoid * pData)143 void OpenGLProgram::SetMaskCoord(const GLvoid* pData)
144 {
145     SetVertexAttrib(mnMaskCoordAttrib, "mask_coord_in", 2, GL_FLOAT, GL_FALSE, 0, pData);
146 }
147 
SetExtrusionVectors(const GLvoid * pData)148 void OpenGLProgram::SetExtrusionVectors(const GLvoid* pData)
149 {
150     SetVertexAttrib(mnExtrusionVectorsAttrib, "extrusion_vectors", 3, GL_FLOAT, GL_FALSE, 0, pData);
151 }
152 
SetVertexColors(std::vector<GLubyte> & rColorVector)153 void OpenGLProgram::SetVertexColors(std::vector<GLubyte>& rColorVector)
154 {
155     SetVertexAttrib(mnVertexColorsAttrib, "vertex_color_in", 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, rColorVector.data());
156 }
157 
SetShaderType(TextureShaderType eTextureShaderType)158 void OpenGLProgram::SetShaderType(TextureShaderType eTextureShaderType)
159 {
160     SetUniform1i("type", GLint(eTextureShaderType));
161 }
162 
SetShaderType(DrawShaderType eDrawShaderType)163 void OpenGLProgram::SetShaderType(DrawShaderType eDrawShaderType)
164 {
165     SetUniform1i("type", GLint(eDrawShaderType));
166 }
167 
GetUniformLocation(const OString & rName)168 GLuint OpenGLProgram::GetUniformLocation( const OString& rName )
169 {
170     auto it = maUniformLocations.find( rName );
171     if( it == maUniformLocations.end() )
172     {
173         GLuint nLocation = glGetUniformLocation( mnId, rName.getStr() );
174         CHECK_GL_ERROR();
175         maUniformLocations[rName] = nLocation;
176         return nLocation;
177     }
178 
179     return it->second;
180 }
181 
DrawArrays(GLenum aMode,std::vector<GLfloat> & aVertices)182 void OpenGLProgram::DrawArrays(GLenum aMode, std::vector<GLfloat>& aVertices)
183 {
184     if (!mbBlending)
185         OpenGLContext::getVCLContext()->state().blend().disable();
186 
187     SetVertices(aVertices.data());
188     glDrawArrays(aMode, 0, aVertices.size() / 2);
189 }
190 
DrawElements(GLenum aMode,GLuint nNumberOfVertices)191 void OpenGLProgram::DrawElements(GLenum aMode, GLuint nNumberOfVertices)
192 {
193     if (!mbBlending)
194         OpenGLContext::getVCLContext()->state().blend().disable();
195 
196     glDrawElements(aMode, nNumberOfVertices, GL_UNSIGNED_INT, nullptr);
197 }
198 
SetUniform1f(const OString & rName,GLfloat v1)199 void OpenGLProgram::SetUniform1f( const OString& rName, GLfloat v1 )
200 {
201     GLuint nUniform = GetUniformLocation( rName );
202     glUniform1f( nUniform, v1 );
203     CHECK_GL_ERROR();
204 }
205 
SetUniform2f(const OString & rName,GLfloat v1,GLfloat v2)206 void OpenGLProgram::SetUniform2f( const OString& rName, GLfloat v1, GLfloat v2 )
207 {
208     GLuint nUniform = GetUniformLocation( rName );
209     glUniform2f( nUniform, v1, v2 );
210     CHECK_GL_ERROR();
211 }
212 
SetUniform1fv(const OString & rName,GLsizei nCount,GLfloat const * aValues)213 void OpenGLProgram::SetUniform1fv( const OString& rName, GLsizei nCount, GLfloat const * aValues )
214 {
215     GLuint nUniform = GetUniformLocation( rName );
216     glUniform1fv( nUniform, nCount, aValues );
217     CHECK_GL_ERROR();
218 }
219 
SetUniform2fv(const OString & rName,GLsizei nCount,GLfloat const * aValues)220 void OpenGLProgram::SetUniform2fv( const OString& rName, GLsizei nCount, GLfloat const * aValues )
221 {
222     GLuint nUniform = GetUniformLocation( rName );
223     glUniform2fv( nUniform, nCount, aValues );
224     CHECK_GL_ERROR();
225 }
226 
SetUniform1i(const OString & rName,GLint v1)227 void OpenGLProgram::SetUniform1i( const OString& rName, GLint v1 )
228 {
229     GLuint nUniform = GetUniformLocation( rName );
230     glUniform1i( nUniform, v1 );
231     CHECK_GL_ERROR();
232 }
233 
SetColor(const OString & rName,Color nColor,sal_uInt8 nTransparency)234 void OpenGLProgram::SetColor( const OString& rName, Color nColor, sal_uInt8 nTransparency )
235 {
236     GLuint nUniform = GetUniformLocation( rName );
237     glUniform4f( nUniform,
238                  nColor.GetRed() / 255.0f,
239                  nColor.GetGreen() / 255.0f,
240                  nColor.GetBlue() / 255.0f,
241                  (100 - nTransparency) * (1.0 / 100) );
242     CHECK_GL_ERROR();
243 
244     if( nTransparency > 0 )
245         SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
246 }
247 
SetColorf(const OString & rName,Color nColor,double fTransparency)248 void OpenGLProgram::SetColorf( const OString& rName, Color nColor, double fTransparency )
249 {
250     GLuint nUniform = GetUniformLocation( rName );
251     glUniform4f( nUniform,
252                  nColor.GetRed() / 255.0f,
253                  nColor.GetGreen() / 255.0f,
254                  nColor.GetBlue() / 255.0f,
255                  (1.0f - fTransparency) );
256     CHECK_GL_ERROR();
257 
258     if( fTransparency > 0.0 )
259         SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
260 }
261 
SetColor(const OString & rName,const Color & rColor)262 void OpenGLProgram::SetColor( const OString& rName, const Color& rColor )
263 {
264     GLuint nUniform = GetUniformLocation( rName );
265     glUniform4f( nUniform,
266                  static_cast<float>(rColor.GetRed()) / 255,
267                  static_cast<float>(rColor.GetGreen()) / 255,
268                  static_cast<float>(rColor.GetBlue()) / 255,
269                  1.0f - static_cast<float>(rColor.GetTransparency()) / 255 );
270     CHECK_GL_ERROR();
271 
272     if( rColor.GetTransparency() > 0 )
273         SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
274 }
275 
SetColorWithIntensity(const OString & rName,const Color & rColor,long nFactor)276 void OpenGLProgram::SetColorWithIntensity( const OString& rName, const Color& rColor, long nFactor )
277 {
278     GLuint nUniform = GetUniformLocation( rName );
279     glUniform4f( nUniform,
280                  static_cast<float>(rColor.GetRed()) * nFactor / 25500.0,
281                  static_cast<float>(rColor.GetGreen()) * nFactor / 25500.0,
282                  static_cast<float>(rColor.GetBlue()) * nFactor / 25500.0,
283                  1.0f );
284     CHECK_GL_ERROR();
285 }
286 
SetTexture(const OString & rName,OpenGLTexture & rTexture)287 void OpenGLProgram::SetTexture( const OString& rName, OpenGLTexture& rTexture )
288 {
289     GLuint nUniform = GetUniformLocation( rName );
290     int nIndex = maTextures.size();
291 
292     glUniform1i( nUniform, nIndex );
293     CHECK_GL_ERROR();
294 
295     OpenGLContext::getVCLContext()->state().texture().active(nIndex);
296 
297     rTexture.Bind();
298     maTextures.push_back(rTexture);
299 }
300 
SetTransform(const OString & rName,const OpenGLTexture & rTexture,const basegfx::B2DPoint & rNull,const basegfx::B2DPoint & rX,const basegfx::B2DPoint & rY)301 void OpenGLProgram::SetTransform(
302     const OString& rName,
303     const OpenGLTexture& rTexture,
304     const basegfx::B2DPoint& rNull,
305     const basegfx::B2DPoint& rX,
306     const basegfx::B2DPoint& rY )
307 {
308     auto nTexWidth = rTexture.GetWidth();
309     auto nTexHeight = rTexture.GetHeight();
310     if (nTexWidth == 0 || nTexHeight == 0)
311         return;
312 
313     GLuint nUniform = GetUniformLocation( rName );
314     const basegfx::B2DVector aXRel = rX - rNull;
315     const basegfx::B2DVector aYRel = rY - rNull;
316     const float aValues[] = {
317         static_cast<float>(aXRel.getX())/nTexWidth,  static_cast<float>(aXRel.getY())/nTexWidth,  0, 0,
318         static_cast<float>(aYRel.getX())/nTexHeight, static_cast<float>(aYRel.getY())/nTexHeight, 0, 0,
319         0,                               0,                               1, 0,
320         static_cast<float>(rNull.getX()),            static_cast<float>(rNull.getY()),            0, 1 };
321     glm::mat4 aMatrix = glm::make_mat4( aValues );
322     glUniformMatrix4fv( nUniform, 1, GL_FALSE, glm::value_ptr( aMatrix ) );
323     CHECK_GL_ERROR();
324 }
325 
SetIdentityTransform(const OString & rName)326 void OpenGLProgram::SetIdentityTransform(const OString& rName)
327 {
328     GLuint nUniform = GetUniformLocation(rName);
329     glm::mat4 aMatrix {};
330     glUniformMatrix4fv(nUniform, 1, GL_FALSE, glm::value_ptr( aMatrix ) );
331     CHECK_GL_ERROR();
332 }
333 
ApplyMatrix(float fWidth,float fHeight,float fPixelOffset)334 void OpenGLProgram::ApplyMatrix(float fWidth, float fHeight, float fPixelOffset)
335 {
336 
337     if (mfLastWidth == fWidth && mfLastHeight == fHeight && mfLastPixelOffset == fPixelOffset)
338         return;
339 
340     mfLastWidth = fWidth;
341     mfLastHeight = fHeight;
342     mfLastPixelOffset = fPixelOffset;
343 
344     GLuint nUniform = GetUniformLocation("mvp");
345 
346     glm::mat4 aMVP = glm::ortho(0.0f, fWidth, fHeight, 0.0f, 0.0f, 1.0f);
347 
348     if (fPixelOffset != 0.0f)
349         aMVP = glm::translate(aMVP, glm::vec3(fPixelOffset, fPixelOffset, 0.0f));
350 
351     glUniformMatrix4fv(nUniform, 1, GL_FALSE, glm::value_ptr(aMVP));
352     CHECK_GL_ERROR();
353 }
354 
SetBlendMode(GLenum nSFactor,GLenum nDFactor)355 void OpenGLProgram::SetBlendMode(GLenum nSFactor, GLenum nDFactor)
356 {
357     OpenGLContext::getVCLContext()->state().blend().enable();
358     OpenGLContext::getVCLContext()->state().blend().func(nSFactor, nDFactor);
359     mbBlending = true;
360 }
361 
DrawTexture(const OpenGLTexture & rTexture)362 void OpenGLProgram::DrawTexture( const OpenGLTexture& rTexture )
363 {
364     if (!rTexture)
365         return;
366 
367     float fWidth = rTexture.GetWidth();
368     float fHeight = rTexture.GetHeight();
369 
370     float fMinX = 0.0f;
371     float fMaxX = fWidth;
372     float fMinY = 0.0f;
373     float fMaxY = fHeight;
374 
375     std::vector<GLfloat> aPosition {
376         fMinX, fMaxY,
377         fMinX, fMinY,
378         fMaxX, fMinY,
379         fMaxX, fMaxY
380     };
381     GLfloat aTexCoord[8];
382 
383     rTexture.GetWholeCoord( aTexCoord );
384     SetTextureCoord( aTexCoord );
385     ApplyMatrix(fWidth, fHeight);
386     DrawArrays(GL_TRIANGLE_FAN, aPosition);
387     CHECK_GL_ERROR();
388 }
389 
390 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
391