0023630: Incorrect NULL check in OpenGl_Workspace::EnableTexture()
[occt.git] / src / OpenGl / OpenGl_Texture.cxx
1 // Created by: Kirill GAVRILOV
2 // Copyright (c) 2012 OPEN CASCADE SAS
3 //
4 // The content of this file is subject to the Open CASCADE Technology Public
5 // License Version 6.5 (the "License"). You may not use the content of this file
6 // except in compliance with the License. Please obtain a copy of the License
7 // at http://www.opencascade.org and read it completely before using this file.
8 //
9 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
10 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
11 //
12 // The Original Code and all software distributed under the License is
13 // distributed on an "AS IS" basis, without warranty of any kind, and the
14 // Initial Developer hereby disclaims all such warranties, including without
15 // limitation, any warranties of merchantability, fitness for a particular
16 // purpose or non-infringement. Please see the License for the specific terms
17 // and conditions governing the rights and limitations under the License.
18
19 #include <OpenGl_Texture.hxx>
20
21 #include <OpenGl_Context.hxx>
22 #include <Graphic3d_TextureParams.hxx>
23 #include <Standard_Assert.hxx>
24 #include <Image_PixMap.hxx>
25
26 IMPLEMENT_STANDARD_HANDLE (OpenGl_Texture, OpenGl_Resource)
27 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_Resource)
28
29 //! Function for getting power of to number larger or equal to input number.
30 //! @param theNumber    number to 'power of two'
31 //! @param theThreshold upper threshold
32 //! @return power of two number
33 inline GLsizei getPowerOfTwo (const GLsizei theNumber,
34                               const GLsizei theThreshold)
35 {
36   for (GLsizei p2 = 2; p2 <= theThreshold; p2 <<= 1)
37   {
38     if (theNumber <= p2)
39     {
40       return p2;
41     }
42   }
43   return theThreshold;
44 }
45
46 // =======================================================================
47 // function : OpenGl_Texture
48 // purpose  :
49 // =======================================================================
50 OpenGl_Texture::OpenGl_Texture (const Handle(Graphic3d_TextureParams)& theParams)
51 : OpenGl_Resource(),
52   myTextureId (NO_TEXTURE),
53   myTarget (GL_TEXTURE_2D),
54   mySizeX (0),
55   mySizeY (0),
56   myTextFormat (GL_FLOAT),
57   myHasMipmaps (Standard_False),
58   myParams     (theParams)
59 {
60   if (myParams.IsNull())
61   {
62     myParams = new Graphic3d_TextureParams();
63   }
64 }
65
66 // =======================================================================
67 // function : ~OpenGl_Texture
68 // purpose  :
69 // =======================================================================
70 OpenGl_Texture::~OpenGl_Texture()
71 {
72   Release (NULL);
73 }
74
75 // =======================================================================
76 // function : HasMipmaps
77 // purpose  :
78 // =======================================================================
79 const Standard_Boolean OpenGl_Texture::HasMipmaps() const
80 {
81   return myHasMipmaps;
82 }
83
84 // =======================================================================
85 // function : GetParams
86 // purpose  :
87 // =======================================================================
88 const Handle(Graphic3d_TextureParams)& OpenGl_Texture::GetParams() const
89 {
90   return myParams;
91 }
92
93 // =======================================================================
94 // function : SetParams
95 // purpose  :
96 // =======================================================================
97 void OpenGl_Texture::SetParams (const Handle(Graphic3d_TextureParams)& theParams)
98 {
99   myParams = theParams;
100 }
101
102 // =======================================================================
103 // function : Create
104 // purpose  :
105 // =======================================================================
106 bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& )
107 {
108   if (myTextureId == NO_TEXTURE)
109   {
110     glGenTextures (1, &myTextureId);
111   }
112   return myTextureId != NO_TEXTURE;
113 }
114
115 // =======================================================================
116 // function : Release
117 // purpose  :
118 // =======================================================================
119 void OpenGl_Texture::Release (const OpenGl_Context* theGlCtx)
120 {
121   if (myTextureId == NO_TEXTURE)
122   {
123     return;
124   }
125
126   // application can not handle this case by exception - this is bug in code
127   Standard_ASSERT_RETURN (theGlCtx != NULL,
128     "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
129
130   glDeleteTextures (1, &myTextureId);
131   myTextureId = NO_TEXTURE;
132   mySizeX = mySizeY = 0;
133 }
134
135 // =======================================================================
136 // function : Bind
137 // purpose  :
138 // =======================================================================
139 void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
140                            const GLenum theTextureUnit) const
141 {
142   if (theCtx->IsGlGreaterEqual (1, 3))
143   {
144     theCtx->core13->glActiveTexture (theTextureUnit);
145   }
146   glBindTexture (myTarget, myTextureId);
147 }
148
149 // =======================================================================
150 // function : Unbind
151 // purpose  :
152 // =======================================================================
153 void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
154                              const GLenum theTextureUnit) const
155 {
156   if (theCtx->IsGlGreaterEqual (1, 3))
157   {
158     theCtx->core13->glActiveTexture (theTextureUnit);
159   }
160   glBindTexture (myTarget, NO_TEXTURE);
161 }
162
163 // =======================================================================
164 // function : Init
165 // purpose  :
166 // =======================================================================
167 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
168                            const Image_PixMap&           theImage,
169                            const Graphic3d_TypeOfTexture theType)
170 {
171   myHasMipmaps = Standard_False;
172   if (theImage.IsEmpty() || !Create (theCtx))
173   {
174     return false;
175   }
176
177   GLenum aTextureFormat = GL_RGBA8;
178   GLenum aPixelFormat   = 0;
179   GLenum aDataType      = 0;
180   switch (theImage.Format())
181   {
182     case Image_PixMap::ImgGrayF:
183     {
184       aTextureFormat = GL_ALPHA8; // GL_R8, GL_R32F
185       aPixelFormat   = GL_ALPHA;  // GL_RED
186       aDataType      = GL_FLOAT;
187       break;
188     }
189     case Image_PixMap::ImgRGBAF:
190     {
191       aTextureFormat = GL_RGBA8; // GL_RGBA32F
192       aPixelFormat   = GL_RGBA;
193       aDataType      = GL_FLOAT;
194       break;
195     }
196     case Image_PixMap::ImgBGRAF:
197     {
198       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
199       {
200         return false;
201       }
202       aTextureFormat = GL_RGBA8; // GL_RGBA32F
203       aPixelFormat   = GL_BGRA;  // equals to GL_BGRA_EXT
204       aDataType      = GL_FLOAT;
205       break;
206     }
207     case Image_PixMap::ImgRGBF:
208     {
209       aTextureFormat = GL_RGB8; // GL_RGB32F
210       aPixelFormat   = GL_RGB;
211       aDataType      = GL_FLOAT;
212       break;
213     }
214     case Image_PixMap::ImgBGRF:
215     {
216       aTextureFormat = GL_RGB8; // GL_RGB32F
217       aPixelFormat   = GL_BGR;  // equals to GL_BGR_EXT
218       aDataType      = GL_FLOAT;
219       break;
220     }
221     case Image_PixMap::ImgRGBA:
222     {
223       aTextureFormat = GL_RGBA8;
224       aPixelFormat   = GL_RGBA;
225       aDataType      = GL_UNSIGNED_BYTE;
226       break;
227     }
228     case Image_PixMap::ImgBGRA:
229     {
230       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
231       {
232         return false;
233       }
234       aTextureFormat = GL_RGBA8;
235       aPixelFormat   = GL_BGRA;  // equals to GL_BGRA_EXT
236       aDataType      = GL_UNSIGNED_BYTE;
237       break;
238     }
239     case Image_PixMap::ImgRGB32:
240     {
241       aTextureFormat = GL_RGB8;
242       aPixelFormat   = GL_RGBA;
243       aDataType      = GL_UNSIGNED_BYTE;
244       break;
245     }
246     case Image_PixMap::ImgBGR32:
247     {
248       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
249       {
250         return false;
251       }
252       aTextureFormat = GL_RGB8;
253       aPixelFormat   = GL_BGRA;  // equals to GL_BGRA_EXT
254       aDataType      = GL_UNSIGNED_BYTE;
255       break;
256     }
257     case Image_PixMap::ImgRGB:
258     {
259       aTextureFormat = GL_RGB8;
260       aPixelFormat   = GL_RGB;
261       aDataType      = GL_UNSIGNED_BYTE;
262       break;
263     }
264     case Image_PixMap::ImgBGR:
265     {
266       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
267       {
268         return false;
269       }
270       aTextureFormat = GL_RGB8;
271       aPixelFormat   = GL_BGR;  // equals to GL_BGR_EXT
272       aDataType      = GL_UNSIGNED_BYTE;
273       break;
274     }
275     case Image_PixMap::ImgGray:
276     {
277       aTextureFormat = GL_ALPHA8; // GL_R8
278       aPixelFormat   = GL_ALPHA;  // GL_RED
279       aDataType      = GL_UNSIGNED_BYTE;
280       break;
281     }
282     default:
283     {
284       return false;
285     }
286   }
287
288   const GLsizei aMaxSize   = theCtx->MaxTextureSize();
289   const GLsizei aWidth     = (GLsizei )theImage.SizeX();
290   const GLsizei aHeight    = (GLsizei )theImage.SizeY();
291
292   // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
293   // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
294   // Trying to create NPOT rextures on such hardware will not fail
295   // but driver will fall back into software rendering,
296   const bool    toForceP2  = !theCtx->IsGlGreaterEqual (3, 0) && !theCtx->arbNPTW;
297   const GLsizei aWidthOut  = toForceP2 ? getPowerOfTwo (aWidth,  aMaxSize) : Min (aWidth,  aMaxSize);
298   const GLsizei aHeightOut = toForceP2 ? getPowerOfTwo (aHeight, aMaxSize) : Min (aHeight, aMaxSize);
299
300   GLint aTestWidth  = 0;
301   GLint aTestHeight = 0;
302
303   glPixelStorei (GL_UNPACK_ALIGNMENT, 1); // ensure alignment will not screw up the party
304   switch (theType)
305   {
306     case Graphic3d_TOT_1D:
307     {
308       myTarget = GL_TEXTURE_1D;
309       Bind (theCtx);
310       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
311       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
312
313       Image_PixMap aCopy;
314       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
315       if (aWidth != aWidthOut)
316       {
317         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), 1)
318           || gluScaleImage (aPixelFormat,
319                             aWidth,    1, aDataType, theImage.Data(),
320                             aWidthOut, 1, aDataType, aCopy.ChangeData()) != 0)
321         {
322           Unbind (theCtx);
323           return false;
324         }
325
326         aDataPtr = (GLvoid* )aCopy.Data();
327       }
328
329       // use proxy to check texture could be created or not
330       glTexImage1D (GL_PROXY_TEXTURE_1D, 0, aTextureFormat,
331                     aWidthOut, 0,
332                     aPixelFormat, aDataType, NULL);
333       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
334       if (aTestWidth == 0)
335       {
336         // no memory or broken input parameters
337         Unbind (theCtx);
338         return false;
339       }
340
341       glTexImage1D (GL_TEXTURE_1D, 0, aTextureFormat,
342                     aWidthOut, 0,
343                     aPixelFormat, aDataType, aDataPtr);
344
345       Unbind (theCtx);
346       return true;
347     }
348     case Graphic3d_TOT_2D:
349     {
350       myTarget = GL_TEXTURE_2D;
351       Bind (theCtx);
352       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
353       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
354
355       Image_PixMap aCopy;
356       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
357       if (aWidth != aWidthOut || aHeight != aHeightOut)
358       {
359         // scale texture
360
361         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), Standard_Size(aHeightOut))
362           || gluScaleImage (aPixelFormat,
363                             aWidth,    aHeight,    aDataType, theImage.Data(),
364                             aWidthOut, aHeightOut, aDataType, aCopy.ChangeData()) != 0)
365         {
366           Unbind (theCtx);
367           return false;
368         }
369
370         aDataPtr = (GLvoid* )aCopy.Data();
371       }
372
373       // use proxy to check texture could be created or not
374       glTexImage2D (GL_PROXY_TEXTURE_2D, 0, aTextureFormat,
375                     aWidthOut, aHeightOut, 0,
376                     aPixelFormat, aDataType, NULL);
377       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
378       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
379       if (aTestWidth == 0 || aTestHeight == 0)
380       {
381         // no memory or broken input parameters
382         Unbind (theCtx);
383         return false;
384       }
385
386       glTexImage2D (GL_TEXTURE_2D, 0, aTextureFormat,
387                     aWidthOut, aHeightOut, 0,
388                     aPixelFormat, aDataType, aDataPtr);
389
390       Unbind (theCtx);
391       return true;
392     }
393     case Graphic3d_TOT_2D_MIPMAP:
394     {
395       myTarget     = GL_TEXTURE_2D;
396       myHasMipmaps = Standard_True;
397       Bind (theCtx);
398       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
399       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
400
401       bool isCreated = gluBuild2DMipmaps (GL_TEXTURE_2D, aTextureFormat,
402                                           aWidth, aHeight,
403                                           aPixelFormat, aDataType, theImage.Data()) == 0;
404       Unbind (theCtx);
405       return isCreated;
406     }
407     default:
408     {
409       return false;
410     }
411   }
412 }