0023813: TKOpenGl, OpenGl_Texture ignores stride image property
[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_ExtFBO.hxx>
22 #include <OpenGl_Context.hxx>
23 #include <Graphic3d_TextureParams.hxx>
24 #include <Standard_Assert.hxx>
25 #include <Image_PixMap.hxx>
26
27 IMPLEMENT_STANDARD_HANDLE (OpenGl_Texture, OpenGl_Resource)
28 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_Resource)
29
30 //! Simple class to reset unpack alignment settings
31 struct OpenGl_UnpackAlignmentSentry
32 {
33
34   //! Reset unpack alignment settings to safe values
35   void Reset()
36   {
37     glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
38     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
39   }
40
41   ~OpenGl_UnpackAlignmentSentry()
42   {
43     Reset();
44   }
45
46 };
47
48 // =======================================================================
49 // function : OpenGl_Texture
50 // purpose  :
51 // =======================================================================
52 OpenGl_Texture::OpenGl_Texture (const Handle(Graphic3d_TextureParams)& theParams)
53 : OpenGl_Resource(),
54   myTextureId (NO_TEXTURE),
55   myTarget (GL_TEXTURE_2D),
56   mySizeX (0),
57   mySizeY (0),
58   myTextFormat (GL_FLOAT),
59   myHasMipmaps (Standard_False),
60   myParams     (theParams)
61 {
62   if (myParams.IsNull())
63   {
64     myParams = new Graphic3d_TextureParams();
65   }
66 }
67
68 // =======================================================================
69 // function : ~OpenGl_Texture
70 // purpose  :
71 // =======================================================================
72 OpenGl_Texture::~OpenGl_Texture()
73 {
74   Release (NULL);
75 }
76
77 // =======================================================================
78 // function : HasMipmaps
79 // purpose  :
80 // =======================================================================
81 const Standard_Boolean OpenGl_Texture::HasMipmaps() const
82 {
83   return myHasMipmaps;
84 }
85
86 // =======================================================================
87 // function : GetParams
88 // purpose  :
89 // =======================================================================
90 const Handle(Graphic3d_TextureParams)& OpenGl_Texture::GetParams() const
91 {
92   return myParams;
93 }
94
95 // =======================================================================
96 // function : SetParams
97 // purpose  :
98 // =======================================================================
99 void OpenGl_Texture::SetParams (const Handle(Graphic3d_TextureParams)& theParams)
100 {
101   myParams = theParams;
102 }
103
104 // =======================================================================
105 // function : Create
106 // purpose  :
107 // =======================================================================
108 bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& )
109 {
110   if (myTextureId == NO_TEXTURE)
111   {
112     glGenTextures (1, &myTextureId);
113   }
114   return myTextureId != NO_TEXTURE;
115 }
116
117 // =======================================================================
118 // function : Release
119 // purpose  :
120 // =======================================================================
121 void OpenGl_Texture::Release (const OpenGl_Context* theGlCtx)
122 {
123   if (myTextureId == NO_TEXTURE)
124   {
125     return;
126   }
127
128   // application can not handle this case by exception - this is bug in code
129   Standard_ASSERT_RETURN (theGlCtx != NULL,
130     "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
131
132   if (theGlCtx->IsValid())
133   {
134     glDeleteTextures (1, &myTextureId);
135   }
136   myTextureId = NO_TEXTURE;
137   mySizeX = mySizeY = 0;
138 }
139
140 // =======================================================================
141 // function : Bind
142 // purpose  :
143 // =======================================================================
144 void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
145                            const GLenum theTextureUnit) const
146 {
147   if (theCtx->IsGlGreaterEqual (1, 3))
148   {
149     theCtx->core13->glActiveTexture (theTextureUnit);
150   }
151   glBindTexture (myTarget, myTextureId);
152 }
153
154 // =======================================================================
155 // function : Unbind
156 // purpose  :
157 // =======================================================================
158 void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
159                              const GLenum theTextureUnit) const
160 {
161   if (theCtx->IsGlGreaterEqual (1, 3))
162   {
163     theCtx->core13->glActiveTexture (theTextureUnit);
164   }
165   glBindTexture (myTarget, NO_TEXTURE);
166 }
167
168 // =======================================================================
169 // function : Init
170 // purpose  :
171 // =======================================================================
172 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
173                            const Image_PixMap&           theImage,
174                            const Graphic3d_TypeOfTexture theType)
175 {
176   myHasMipmaps = Standard_False;
177   if (theImage.IsEmpty() || !Create (theCtx))
178   {
179     return false;
180   }
181
182   myTextFormat = GL_RGBA8;
183   GLenum aPixelFormat = 0;
184   GLenum aDataType    = 0;
185   switch (theImage.Format())
186   {
187     case Image_PixMap::ImgGrayF:
188     {
189       myTextFormat = GL_ALPHA8; // GL_R8, GL_R32F
190       aPixelFormat = GL_ALPHA;  // GL_RED
191       aDataType    = GL_FLOAT;
192       break;
193     }
194     case Image_PixMap::ImgRGBAF:
195     {
196       myTextFormat = GL_RGBA8; // GL_RGBA32F
197       aPixelFormat = GL_RGBA;
198       aDataType    = GL_FLOAT;
199       break;
200     }
201     case Image_PixMap::ImgBGRAF:
202     {
203       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
204       {
205         return false;
206       }
207       myTextFormat = GL_RGBA8; // GL_RGBA32F
208       aPixelFormat = GL_BGRA;  // equals to GL_BGRA_EXT
209       aDataType    = GL_FLOAT;
210       break;
211     }
212     case Image_PixMap::ImgRGBF:
213     {
214       myTextFormat = GL_RGB8; // GL_RGB32F
215       aPixelFormat = GL_RGB;
216       aDataType    = GL_FLOAT;
217       break;
218     }
219     case Image_PixMap::ImgBGRF:
220     {
221       myTextFormat = GL_RGB8; // GL_RGB32F
222       aPixelFormat = GL_BGR;  // equals to GL_BGR_EXT
223       aDataType    = GL_FLOAT;
224       break;
225     }
226     case Image_PixMap::ImgRGBA:
227     {
228       myTextFormat = GL_RGBA8;
229       aPixelFormat = GL_RGBA;
230       aDataType    = GL_UNSIGNED_BYTE;
231       break;
232     }
233     case Image_PixMap::ImgBGRA:
234     {
235       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
236       {
237         return false;
238       }
239       myTextFormat = GL_RGBA8;
240       aPixelFormat = GL_BGRA;  // equals to GL_BGRA_EXT
241       aDataType    = GL_UNSIGNED_BYTE;
242       break;
243     }
244     case Image_PixMap::ImgRGB32:
245     {
246       myTextFormat = GL_RGB8;
247       aPixelFormat = GL_RGBA;
248       aDataType    = GL_UNSIGNED_BYTE;
249       break;
250     }
251     case Image_PixMap::ImgBGR32:
252     {
253       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
254       {
255         return false;
256       }
257       myTextFormat = GL_RGB8;
258       aPixelFormat = GL_BGRA;  // equals to GL_BGRA_EXT
259       aDataType    = GL_UNSIGNED_BYTE;
260       break;
261     }
262     case Image_PixMap::ImgRGB:
263     {
264       myTextFormat = GL_RGB8;
265       aPixelFormat = GL_RGB;
266       aDataType    = GL_UNSIGNED_BYTE;
267       break;
268     }
269     case Image_PixMap::ImgBGR:
270     {
271       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
272       {
273         return false;
274       }
275       myTextFormat = GL_RGB8;
276       aPixelFormat = GL_BGR;  // equals to GL_BGR_EXT
277       aDataType    = GL_UNSIGNED_BYTE;
278       break;
279     }
280     case Image_PixMap::ImgGray:
281     {
282       myTextFormat = GL_ALPHA8; // GL_R8
283       aPixelFormat = GL_ALPHA;  // GL_RED
284       aDataType    = GL_UNSIGNED_BYTE;
285       break;
286     }
287     default:
288     {
289       return false;
290     }
291   }
292
293   const GLsizei aMaxSize   = theCtx->MaxTextureSize();
294   const GLsizei aWidth     = (GLsizei )theImage.SizeX();
295   const GLsizei aHeight    = (GLsizei )theImage.SizeY();
296
297   // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
298   // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
299   // Trying to create NPOT rextures on such hardware will not fail
300   // but driver will fall back into software rendering,
301   const bool    toForceP2  = !theCtx->IsGlGreaterEqual (3, 0) && !theCtx->arbNPTW;
302   const GLsizei aWidthOut  = toForceP2 ? OpenGl_Context::GetPowerOfTwo (aWidth,  aMaxSize) : Min (aWidth,  aMaxSize);
303   const GLsizei aHeightOut = toForceP2 ? OpenGl_Context::GetPowerOfTwo (aHeight, aMaxSize) : Min (aHeight, aMaxSize);
304
305   GLint aTestWidth  = 0;
306   GLint aTestHeight = 0;
307
308   // setup the alignment
309   OpenGl_UnpackAlignmentSentry anUnpackSentry;
310   const GLint anAligment = Min ((GLint )theImage.MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
311   glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
312
313   // notice that GL_UNPACK_ROW_LENGTH is not available on OpenGL ES 2.0 without GL_EXT_unpack_subimage extension
314   const GLint anExtraBytes = GLint(theImage.RowExtraBytes());
315   const GLint aPixelsWidth = GLint(theImage.SizeRowBytes() / theImage.SizePixelBytes());
316   glPixelStorei (GL_UNPACK_ROW_LENGTH, (anExtraBytes >= anAligment) ? aPixelsWidth : 0);
317
318   switch (theType)
319   {
320     case Graphic3d_TOT_1D:
321     {
322       myTarget = GL_TEXTURE_1D;
323       Bind (theCtx);
324       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
325       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
326
327       Image_PixMap aCopy;
328       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
329       if (aWidth != aWidthOut)
330       {
331         glPixelStorei (GL_PACK_ALIGNMENT,  1);
332         glPixelStorei (GL_PACK_ROW_LENGTH, 0);
333         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), 1)
334           || gluScaleImage (aPixelFormat,
335                             aWidth,    1, aDataType, theImage.Data(),
336                             aWidthOut, 1, aDataType, aCopy.ChangeData()) != 0)
337         {
338           Unbind (theCtx);
339           return false;
340         }
341
342         aDataPtr = (GLvoid* )aCopy.Data();
343         anUnpackSentry.Reset();
344       }
345
346       // use proxy to check texture could be created or not
347       glTexImage1D (GL_PROXY_TEXTURE_1D, 0, myTextFormat,
348                     aWidthOut, 0,
349                     aPixelFormat, aDataType, NULL);
350       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
351       if (aTestWidth == 0)
352       {
353         // no memory or broken input parameters
354         Unbind (theCtx);
355         return false;
356       }
357
358       glTexImage1D (GL_TEXTURE_1D, 0, myTextFormat,
359                     aWidthOut, 0,
360                     aPixelFormat, aDataType, aDataPtr);
361       if (glGetError() != GL_NO_ERROR)
362       {
363         Unbind (theCtx);
364         return false;
365       }
366
367       mySizeX = aWidthOut;
368       mySizeY = 1;
369
370       Unbind (theCtx);
371       return true;
372     }
373     case Graphic3d_TOT_2D:
374     {
375       myTarget = GL_TEXTURE_2D;
376       Bind (theCtx);
377       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
378       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
379
380       Image_PixMap aCopy;
381       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
382       if (aWidth != aWidthOut || aHeight != aHeightOut)
383       {
384         // scale texture
385         glPixelStorei (GL_PACK_ALIGNMENT,  1);
386         glPixelStorei (GL_PACK_ROW_LENGTH, 0);
387         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), Standard_Size(aHeightOut))
388           || gluScaleImage (aPixelFormat,
389                             aWidth,    aHeight,    aDataType, theImage.Data(),
390                             aWidthOut, aHeightOut, aDataType, aCopy.ChangeData()) != 0)
391         {
392           Unbind (theCtx);
393           return false;
394         }
395
396         aDataPtr = (GLvoid* )aCopy.Data();
397         anUnpackSentry.Reset();
398       }
399
400       // use proxy to check texture could be created or not
401       glTexImage2D (GL_PROXY_TEXTURE_2D, 0, myTextFormat,
402                     aWidthOut, aHeightOut, 0,
403                     aPixelFormat, aDataType, NULL);
404       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
405       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
406       if (aTestWidth == 0 || aTestHeight == 0)
407       {
408         // no memory or broken input parameters
409         Unbind (theCtx);
410         return false;
411       }
412
413       glTexImage2D (GL_TEXTURE_2D, 0, myTextFormat,
414                     aWidthOut, aHeightOut, 0,
415                     aPixelFormat, aDataType, aDataPtr);
416       if (glGetError() != GL_NO_ERROR)
417       {
418         Unbind (theCtx);
419         return false;
420       }
421
422       mySizeX = aWidthOut;
423       mySizeY = aHeightOut;
424
425       Unbind (theCtx);
426       return true;
427     }
428     case Graphic3d_TOT_2D_MIPMAP:
429     {
430       myTarget     = GL_TEXTURE_2D;
431       myHasMipmaps = Standard_True;
432       Bind (theCtx);
433       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
434       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
435
436       if (theCtx->extFBO != NULL
437        && aWidth == aWidthOut && aHeight == aHeightOut)
438       {
439         // use proxy to check texture could be created or not
440         glTexImage2D (GL_PROXY_TEXTURE_2D, 0, myTextFormat,
441                       aWidthOut, aHeightOut, 0,
442                       aPixelFormat, aDataType, NULL);
443         glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
444         glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
445         if (aTestWidth == 0 || aTestHeight == 0)
446         {
447           // no memory or broken input parameters
448           Unbind (theCtx);
449           return false;
450         }
451
452         // upload main picture
453         glTexImage2D (GL_TEXTURE_2D, 0, myTextFormat,
454                       aWidthOut, aHeightOut, 0,
455                       aPixelFormat, aDataType, theImage.Data());
456         if (glGetError() != GL_NO_ERROR)
457         {
458           Unbind (theCtx);
459           return false;
460         }
461
462         mySizeX = aWidthOut;
463         mySizeY = aHeightOut;
464
465         // generate mipmaps
466         //glHint (GL_GENERATE_MIPMAP_HINT, GL_NICEST);
467         theCtx->extFBO->glGenerateMipmapEXT (GL_TEXTURE_2D);
468
469         Unbind (theCtx);
470         return true;
471       }
472       else
473       {
474         bool isCreated = gluBuild2DMipmaps (GL_TEXTURE_2D, myTextFormat,
475                                             aWidth, aHeight,
476                                             aPixelFormat, aDataType, theImage.Data()) == 0;
477         if (isCreated)
478         {
479           glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &mySizeX);
480           glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &mySizeY);
481         }
482
483         Unbind (theCtx);
484         return isCreated;
485       }
486     }
487     default:
488     {
489       return false;
490     }
491   }
492 }