0030537: Visualization - wrapping text in font text formatter
[occt.git] / src / OpenGl / OpenGl_Texture.cxx
1 // Created by: Kirill GAVRILOV
2 // Copyright (c) 2013-2014 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <OpenGl_Texture.hxx>
16
17 #include <OpenGl_ArbFBO.hxx>
18 #include <OpenGl_Context.hxx>
19 #include <OpenGl_GlCore32.hxx>
20 #include <OpenGl_Sampler.hxx>
21 #include <Graphic3d_TextureParams.hxx>
22 #include <TCollection_ExtendedString.hxx>
23 #include <Standard_Assert.hxx>
24 #include <Image_CompressedPixMap.hxx>
25 #include <Image_PixMap.hxx>
26 #include <Image_SupportedFormats.hxx>
27
28 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_NamedResource)
29
30 namespace
31 {
32
33 //! Simple class to reset unpack alignment settings
34 struct OpenGl_UnpackAlignmentSentry
35 {
36
37   //! Reset unpack alignment settings to safe values
38   static void Reset()
39   {
40     glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
41   #if !defined(GL_ES_VERSION_2_0)
42     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
43   #endif
44   }
45
46   OpenGl_UnpackAlignmentSentry() {}
47
48   ~OpenGl_UnpackAlignmentSentry()
49   {
50     Reset();
51   }
52
53 };
54
55 //! Compute the upper mipmap level for complete mipmap set (e.g. till the 1x1 level).
56 static Standard_Integer computeUpperMipMapLevel (Standard_Integer theSize)
57 {
58   for (Standard_Integer aMipIter = 0;; ++aMipIter, theSize /= 2)
59   {
60     if (theSize <= 1)
61     {
62       return aMipIter;
63     }
64   }
65 }
66
67 //! Compute the upper mipmap level for complete mipmap set (e.g. till the 1x1 level).
68 static Standard_Integer computeUpperMipMapLevel (Standard_Integer theSizeX, Standard_Integer theSizeY)
69 {
70   return computeUpperMipMapLevel (Max (theSizeX, theSizeY));
71 }
72
73 //! Compute size of the smallest defined mipmap level (for verbose messages).
74 static Graphic3d_Vec2i computeSmallestMipMapSize (const Graphic3d_Vec2i& theBaseSize, Standard_Integer theMaxLevel)
75 {
76   Graphic3d_Vec2i aMipSizeXY = theBaseSize;
77   for (Standard_Integer aMipIter = 0;; ++aMipIter)
78   {
79     if (aMipIter > theMaxLevel)
80     {
81       return aMipSizeXY;
82     }
83
84     aMipSizeXY /= 2;
85     if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
86     if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
87   }
88 }
89
90 }
91
92 // =======================================================================
93 // function : OpenGl_Texture
94 // purpose  :
95 // =======================================================================
96 OpenGl_Texture::OpenGl_Texture (const TCollection_AsciiString& theResourceId,
97                                 const Handle(Graphic3d_TextureParams)& theParams)
98 : OpenGl_NamedResource (theResourceId),
99   mySampler (new OpenGl_Sampler (theParams)),
100   myRevision (0),
101   myTextureId (NO_TEXTURE),
102   myTarget (GL_TEXTURE_2D),
103   mySizeX (0),
104   mySizeY (0),
105   mySizeZ (0),
106   myTextFormat (GL_RGBA),
107   mySizedFormat(GL_RGBA8),
108   myNbSamples  (1),
109   myMaxMipLevel(0),
110   myIsAlpha    (false),
111   myIsTopDown  (true)
112 {
113   //
114 }
115
116 // =======================================================================
117 // function : ~OpenGl_Texture
118 // purpose  :
119 // =======================================================================
120 OpenGl_Texture::~OpenGl_Texture()
121 {
122   Release (NULL);
123 }
124
125 // =======================================================================
126 // function : Create
127 // purpose  :
128 // =======================================================================
129 bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& theCtx)
130 {
131   if (myTextureId != NO_TEXTURE)
132   {
133     return true;
134   }
135
136   theCtx->core11fwd->glGenTextures (1, &myTextureId);
137   if (myTextureId == NO_TEXTURE)
138   {
139     return false;
140   }
141
142   //mySampler->Create (theCtx); // do not create sampler object by default
143   return true;
144 }
145
146 // =======================================================================
147 // function : Release
148 // purpose  :
149 // =======================================================================
150 void OpenGl_Texture::Release (OpenGl_Context* theGlCtx)
151 {
152   mySampler->Release (theGlCtx);
153   if (myTextureId == NO_TEXTURE)
154   {
155     return;
156   }
157
158   // application can not handle this case by exception - this is bug in code
159   Standard_ASSERT_RETURN (theGlCtx != NULL,
160     "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
161
162   if (theGlCtx->IsValid())
163   {
164     glDeleteTextures (1, &myTextureId);
165   }
166   myTextureId = NO_TEXTURE;
167   mySizeX = mySizeY = mySizeZ = 0;
168 }
169
170 // =======================================================================
171 // function : applyDefaultSamplerParams
172 // purpose  :
173 // =======================================================================
174 void OpenGl_Texture::applyDefaultSamplerParams (const Handle(OpenGl_Context)& theCtx)
175 {
176   OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), NULL, myTarget, myMaxMipLevel);
177   if (mySampler->IsValid() && !mySampler->IsImmutable())
178   {
179     OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), mySampler.get(), myTarget, myMaxMipLevel);
180   }
181 }
182
183 // =======================================================================
184 // function : Bind
185 // purpose  :
186 // =======================================================================
187 void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
188                            const Graphic3d_TextureUnit   theTextureUnit) const
189 {
190   if (theCtx->core15fwd != NULL)
191   {
192     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
193   }
194   mySampler->Bind (theCtx, theTextureUnit);
195   glBindTexture (myTarget, myTextureId);
196 }
197
198 // =======================================================================
199 // function : Unbind
200 // purpose  :
201 // =======================================================================
202 void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
203                              const Graphic3d_TextureUnit   theTextureUnit) const
204 {
205   if (theCtx->core15fwd != NULL)
206   {
207     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
208   }
209   mySampler->Unbind (theCtx, theTextureUnit);
210   glBindTexture (myTarget, NO_TEXTURE);
211 }
212
213 //=======================================================================
214 //function : InitSamplerObject
215 //purpose  :
216 //=======================================================================
217 bool OpenGl_Texture::InitSamplerObject (const Handle(OpenGl_Context)& theCtx)
218 {
219   return myTextureId != NO_TEXTURE
220       && mySampler->Init (theCtx, *this);
221 }
222
223 // =======================================================================
224 // function : Init
225 // purpose  :
226 // =======================================================================
227 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
228                            const OpenGl_TextureFormat&   theFormat,
229                            const Graphic3d_Vec2i&        theSizeXY,
230                            const Graphic3d_TypeOfTexture theType,
231                            const Image_PixMap*           theImage)
232 {
233   if (theSizeXY.x() < 1
234    || theSizeXY.y() < 1)
235   {
236     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
237                          "Error: texture of 0 size cannot be created.");
238     Release (theCtx.get());
239     return false;
240   }
241
242 #if !defined(GL_ES_VERSION_2_0)
243   const GLenum aTarget = theType == Graphic3d_TOT_1D
244                        ? GL_TEXTURE_1D
245                        : GL_TEXTURE_2D;
246 #else
247   const GLenum aTarget = GL_TEXTURE_2D;
248 #endif
249   const bool toPatchExisting = IsValid()
250                             && myTextFormat == theFormat.PixelFormat()
251                             && myTarget == aTarget
252                             && HasMipmaps() == (theType == Graphic3d_TOT_2D_MIPMAP)
253                             && mySizeX  == theSizeXY.x()
254                             && (mySizeY == theSizeXY.y() || theType == Graphic3d_TOT_1D);
255   if (!Create (theCtx))
256   {
257     Release (theCtx.get());
258     return false;
259   }
260
261   if (theImage != NULL)
262   {
263     myIsAlpha = theImage->Format() == Image_Format_Alpha
264              || theImage->Format() == Image_Format_AlphaF;
265     myIsTopDown = theImage->IsTopDown();
266   }
267   else
268   {
269     myIsAlpha = theFormat.PixelFormat() == GL_ALPHA;
270   }
271
272   myMaxMipLevel = theType == Graphic3d_TOT_2D_MIPMAP && theCtx->arbFBO != NULL
273                 ? computeUpperMipMapLevel (theSizeXY.x(), theSizeXY.y())
274                 : 0;
275   myTextFormat  = theFormat.PixelFormat();
276   mySizedFormat = theFormat.InternalFormat();
277   myNbSamples   = 1;
278 #if !defined(GL_ES_VERSION_2_0)
279   const GLint anIntFormat  = theFormat.InternalFormat();
280 #else
281   // ES 2.0 does not support sized formats and format conversions - them detected from data type
282   const GLint anIntFormat  = theCtx->IsGlGreaterEqual (3, 0) ? theFormat.InternalFormat() : theFormat.PixelFormat();
283 #endif
284
285   if (theFormat.DataType() == GL_FLOAT
286   && !theCtx->arbTexFloat)
287   {
288     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
289                          "Error: floating-point textures are not supported by hardware.");
290     Release (theCtx.get());
291     return false;
292   }
293
294   const GLsizei aMaxSize = theCtx->MaxTextureSize();
295   if (theSizeXY.x() > aMaxSize
296    || theSizeXY.y() > aMaxSize)
297   {
298     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
299                          TCollection_AsciiString ("Error: Texture dimension - ") + theSizeXY.x() + "x" + theSizeXY.y()
300                        + " exceeds hardware limits (" + aMaxSize + "x" + aMaxSize + ")");
301     Release (theCtx.get());
302     return false;
303   }
304 #if !defined(GL_ES_VERSION_2_0)
305   else if (!theCtx->IsGlGreaterEqual (3, 0) && !theCtx->arbNPTW)
306   {
307     // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
308     // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
309     // Trying to create NPOT textures on such hardware will not fail
310     // but driver will fall back into software rendering,
311     const GLsizei aWidthP2  = OpenGl_Context::GetPowerOfTwo (theSizeXY.x(), aMaxSize);
312     const GLsizei aHeightP2 = OpenGl_Context::GetPowerOfTwo (theSizeXY.y(), aMaxSize);
313     if (theSizeXY.x() != aWidthP2
314      || (theType != Graphic3d_TOT_1D && theSizeXY.y() != aHeightP2))
315     {
316       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
317                            TCollection_AsciiString ("Error: NPOT Textures (") + theSizeXY.x() + "x" + theSizeXY.y() + ")"
318                            " are not supported by hardware.");
319       Release (theCtx.get());
320       return false;
321     }
322   }
323 #else
324   else if (!theCtx->IsGlGreaterEqual (3, 0) && theType == Graphic3d_TOT_2D_MIPMAP)
325   {
326     // Mipmap NPOT textures are not supported by OpenGL ES 2.0.
327     const GLsizei aWidthP2  = OpenGl_Context::GetPowerOfTwo (theSizeXY.x(), aMaxSize);
328     const GLsizei aHeightP2 = OpenGl_Context::GetPowerOfTwo (theSizeXY.y(), aMaxSize);
329     if (theSizeXY.x() != aWidthP2
330      || theSizeXY.y() != aHeightP2)
331     {
332       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
333                            TCollection_AsciiString ("Error: Mipmap NPOT Textures (") + theSizeXY.x() + "x" + theSizeXY.y() + ")"
334                            " are not supported by OpenGL ES 2.0");
335       Release (theCtx.get());
336       return false;
337     }
338   }
339 #endif
340
341 #if !defined(GL_ES_VERSION_2_0)
342   GLint aTestWidth  = 0, aTestHeight = 0;
343 #endif
344   GLvoid* aDataPtr = (theImage != NULL) ? (GLvoid* )theImage->Data() : NULL;
345
346   // setup the alignment
347   OpenGl_UnpackAlignmentSentry anUnpackSentry;
348   (void)anUnpackSentry; // avoid compiler warning
349
350   if (aDataPtr != NULL)
351   {
352     const GLint anAligment = Min ((GLint )theImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
353     glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
354
355   #if !defined(GL_ES_VERSION_2_0)
356     // notice that GL_UNPACK_ROW_LENGTH is not available on OpenGL ES 2.0 without GL_EXT_unpack_subimage extension
357     const GLint anExtraBytes = GLint(theImage->RowExtraBytes());
358     const GLint aPixelsWidth = GLint(theImage->SizeRowBytes() / theImage->SizePixelBytes());
359     glPixelStorei (GL_UNPACK_ROW_LENGTH, (anExtraBytes >= anAligment) ? aPixelsWidth : 0);
360   #endif
361   }
362
363   myTarget = aTarget;
364   switch (theType)
365   {
366     case Graphic3d_TOT_1D:
367     {
368     #if !defined(GL_ES_VERSION_2_0)
369       Bind (theCtx);
370       applyDefaultSamplerParams (theCtx);
371       if (toPatchExisting)
372       {
373         glTexSubImage1D (GL_TEXTURE_1D, 0, 0,
374                          theSizeXY.x(), theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
375         Unbind (theCtx);
376         return true;
377       }
378
379       // use proxy to check texture could be created or not
380       glTexImage1D (GL_PROXY_TEXTURE_1D, 0, anIntFormat,
381                     theSizeXY.x(), 0,
382                     theFormat.PixelFormat(), theFormat.DataType(), NULL);
383       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
384       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
385       if (aTestWidth == 0)
386       {
387         // no memory or broken input parameters
388         Unbind (theCtx);
389         Release (theCtx.operator->());
390         return false;
391       }
392
393       glTexImage1D (GL_TEXTURE_1D, 0, anIntFormat,
394                     theSizeXY.x(), 0,
395                     theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
396       if (glGetError() != GL_NO_ERROR)
397       {
398         Unbind (theCtx);
399         Release (theCtx.get());
400         return false;
401       }
402
403       mySizeX = theSizeXY.x();
404       mySizeY = 1;
405
406       Unbind (theCtx);
407       return true;
408     #else
409       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
410                            "Error: 1D textures are not supported by hardware.");
411       Release (theCtx.get());
412       return false;
413     #endif
414     }
415     case Graphic3d_TOT_2D:
416     case Graphic3d_TOT_2D_MIPMAP:
417     {
418       Bind (theCtx);
419       applyDefaultSamplerParams (theCtx);
420       if (toPatchExisting)
421       {
422         glTexSubImage2D (GL_TEXTURE_2D, 0,
423                          0, 0,
424                          theSizeXY.x(), theSizeXY.y(),
425                          theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
426
427         if (myMaxMipLevel > 0)
428         {
429           // generate mipmaps
430           theCtx->arbFBO->glGenerateMipmap (GL_TEXTURE_2D);
431           if (glGetError() != GL_NO_ERROR)
432           {
433             myMaxMipLevel = 0;
434           }
435         }
436
437         Unbind (theCtx);
438         return true;
439       }
440
441     #if !defined(GL_ES_VERSION_2_0)
442       // use proxy to check texture could be created or not
443       glTexImage2D (GL_PROXY_TEXTURE_2D, 0, anIntFormat,
444                     theSizeXY.x(), theSizeXY.y(), 0,
445                     theFormat.PixelFormat(), theFormat.DataType(), NULL);
446       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
447       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
448       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
449       if (aTestWidth == 0 || aTestHeight == 0)
450       {
451         // no memory or broken input parameters
452         Unbind (theCtx);
453         Release (theCtx.get());
454         return false;
455       }
456     #endif
457
458       glTexImage2D (GL_TEXTURE_2D, 0, anIntFormat,
459                     theSizeXY.x(), theSizeXY.y(), 0,
460                     theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
461       GLenum anErr = glGetError();
462       if (anErr != GL_NO_ERROR)
463       {
464         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
465                              TCollection_AsciiString ("Error: 2D texture ") + theSizeXY.x() + "x" + theSizeXY.y()
466                                                    + " IF: " + int(anIntFormat) + " PF: " + int(theFormat.PixelFormat())
467                                                    + " DT: " + int(theFormat.DataType())
468                                                    + " can not be created with error " + int(anErr) + ".");
469         Unbind (theCtx);
470         Release (theCtx.get());
471         return false;
472       }
473
474       mySizeX = theSizeXY.x();
475       mySizeY = theSizeXY.y();
476
477       if (myMaxMipLevel > 0)
478       {
479         // generate mipmaps
480         //glHint (GL_GENERATE_MIPMAP_HINT, GL_NICEST);
481         theCtx->arbFBO->glGenerateMipmap (GL_TEXTURE_2D);
482         anErr = glGetError();
483         if (anErr != GL_NO_ERROR)
484         {
485           myMaxMipLevel = 0;
486           theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
487                                "Warning: generating mipmaps requires GL_ARB_framebuffer_object extension which is missing.");
488         }
489       }
490
491       Unbind (theCtx);
492       return true;
493     }
494     case Graphic3d_TOT_CUBEMAP:
495     {
496       Unbind (theCtx);
497       Release (theCtx.get());
498       return false;
499     }
500   }
501
502   Release (theCtx.get());
503   return false;
504 }
505
506 // =======================================================================
507 // function : Init
508 // purpose  :
509 // =======================================================================
510 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
511                            const Image_PixMap&           theImage,
512                            const Graphic3d_TypeOfTexture theType,
513                            const Standard_Boolean        theIsColorMap)
514 {
515   if (theImage.IsEmpty())
516   {
517     Release (theCtx.get());
518     return false;
519   }
520
521   const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theImage.Format(), theIsColorMap);
522   if (!aFormat.IsValid())
523   {
524     Release (theCtx.get());
525     return false;
526   }
527
528   return Init (theCtx, aFormat, Graphic3d_Vec2i ((Standard_Integer)theImage.SizeX(), (Standard_Integer)theImage.SizeY()),
529                theType, &theImage);
530 }
531
532 // =======================================================================
533 // function : Init
534 // purpose  :
535 // =======================================================================
536 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)&       theCtx,
537                            const Handle(Graphic3d_TextureMap)& theTextureMap)
538 {
539   if (theTextureMap.IsNull())
540   {
541     return false;
542   }
543
544   switch (theTextureMap->Type())
545   {
546     case Graphic3d_TOT_CUBEMAP:
547     {
548       return InitCubeMap (theCtx, Handle(Graphic3d_CubeMap)::DownCast(theTextureMap),
549                           0, Image_Format_RGB, false, theTextureMap->IsColorMap());
550     }
551     default:
552     {
553       if (theCtx->SupportedTextureFormats()->HasCompressed()
554       && !theCtx->caps->compressedTexturesDisable)
555       {
556         if (Handle(Image_CompressedPixMap) aCompressed = theTextureMap->GetCompressedImage (theCtx->SupportedTextureFormats()))
557         {
558           return InitCompressed (theCtx, *aCompressed, theTextureMap->IsColorMap());
559         }
560       }
561
562       Handle(Image_PixMap) anImage = theTextureMap->GetImage (theCtx->SupportedTextureFormats());
563       if (anImage.IsNull())
564       {
565         return false;
566       }
567       return Init (theCtx, *anImage, theTextureMap->Type(), theTextureMap->IsColorMap());
568     }
569   }
570 }
571
572 // =======================================================================
573 // function : InitCompressed
574 // purpose  :
575 // =======================================================================
576 bool OpenGl_Texture::InitCompressed (const Handle(OpenGl_Context)& theCtx,
577                                      const Image_CompressedPixMap& theImage,
578                                      const Standard_Boolean        theIsColorMap)
579 {
580   if (theImage.SizeX() < 1
581    || theImage.SizeY() < 1
582    || theImage.FaceData().IsNull())
583   {
584     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
585                          "Error: texture of 0 size cannot be created.");
586     Release (theCtx.get());
587     return false;
588   }
589   if (theImage.SizeX() > theCtx->MaxTextureSize()
590    || theImage.SizeY() > theCtx->MaxTextureSize())
591   {
592     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
593                          TCollection_AsciiString ("Error: Texture dimension - ") + theImage.SizeX() + "x" + theImage.SizeY()
594                        + " exceeds hardware limits (" + theCtx->MaxTextureSize() + "x" + theCtx->MaxTextureSize() + ")");
595     Release (theCtx.get());
596     return false;
597   }
598
599   const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindCompressedFormat (theCtx, theImage.CompressedFormat(), theIsColorMap);
600   if (!aFormat.IsValid())
601   {
602     Release (theCtx.get());
603     return false;
604   }
605
606   if (!Create (theCtx))
607   {
608     return false;
609   }
610
611   myTarget = GL_TEXTURE_2D;
612   myNbSamples = 1;
613   myTextFormat  = aFormat.Format();
614   mySizedFormat = aFormat.Internal();
615   myIsTopDown = theImage.IsTopDown();
616   mySizeX = theImage.SizeX();
617   mySizeY = theImage.SizeY();
618   myMaxMipLevel = Max (theImage.MipMaps().Size() - 1, 0);
619   if (myMaxMipLevel > 0
620   && !theImage.IsCompleteMipMapSet())
621   {
622     const Graphic3d_Vec2i aMipSize = computeSmallestMipMapSize (Graphic3d_Vec2i (mySizeX, mySizeY), myMaxMipLevel);
623     if (!theCtx->HasTextureBaseLevel())
624     {
625       myMaxMipLevel = 0;
626       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 0, GL_DEBUG_SEVERITY_MEDIUM,
627                            TCollection_AsciiString ("Warning: compressed 2D texture ") + myResourceId + " " + mySizeX + "x" + mySizeY
628                            + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y() + "; mipmaps will be ignored");
629     }
630     else
631     {
632       Message::SendTrace (TCollection_AsciiString ("Warning: compressed 2D texture ") + myResourceId + " " + mySizeX + "x" + mySizeY
633                           + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y());
634     }
635   }
636
637   Bind (theCtx);
638   applyDefaultSamplerParams (theCtx);
639
640   // setup the alignment
641   OpenGl_UnpackAlignmentSentry::Reset();
642
643   Graphic3d_Vec2i aMipSizeXY (theImage.SizeX(), theImage.SizeY());
644   const Standard_Byte* aData = theImage.FaceData()->Data();
645   for (Standard_Integer aMipIter = 0; aMipIter <= myMaxMipLevel; ++aMipIter)
646   {
647     const Standard_Integer aMipLength = theImage.MipMaps().Value (aMipIter);
648     theCtx->Functions()->glCompressedTexImage2D (GL_TEXTURE_2D, aMipIter, mySizedFormat, aMipSizeXY.x(), aMipSizeXY.y(), 0, aMipLength, aData);
649     const GLenum aTexImgErr = glGetError();
650     if (aTexImgErr != GL_NO_ERROR)
651     {
652       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
653                            TCollection_AsciiString ("Error: 2D compressed texture ") + aMipSizeXY.x() + "x" + aMipSizeXY.y()
654                            + " IF: " + int(aFormat.Internal()) + " PF: " + int(aFormat.PixelFormat())
655                            + " DT: " + int(aFormat.DataType())
656                            + " can not be created with error " + int(aTexImgErr) + ".");
657       Unbind (theCtx);
658       Release (theCtx.get());
659       return false;
660     }
661
662     aData += aMipLength;
663     aMipSizeXY /= 2;
664     if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
665     if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
666   }
667
668   Unbind (theCtx);
669   return true;
670 }
671
672 // =======================================================================
673 // function : Init2DMultisample
674 // purpose  :
675 // =======================================================================
676 bool OpenGl_Texture::Init2DMultisample (const Handle(OpenGl_Context)& theCtx,
677                                         const GLsizei                 theNbSamples,
678                                         const GLint                   theTextFormat,
679                                         const GLsizei                 theSizeX,
680                                         const GLsizei                 theSizeY)
681 {
682   if (!Create (theCtx)
683     || theNbSamples > theCtx->MaxMsaaSamples()
684     || theNbSamples < 1)
685   {
686     return false;
687   }
688
689   myNbSamples = OpenGl_Context::GetPowerOfTwo (theNbSamples, theCtx->MaxMsaaSamples());
690   myTarget = GL_TEXTURE_2D_MULTISAMPLE;
691   myMaxMipLevel = 0;
692   if(theSizeX > theCtx->MaxTextureSize()
693   || theSizeY > theCtx->MaxTextureSize())
694   {
695     return false;
696   }
697
698   Bind (theCtx);
699   //myTextFormat = theTextFormat;
700   mySizedFormat = theTextFormat;
701 #if !defined(GL_ES_VERSION_2_0)
702   if (theCtx->Functions()->glTexStorage2DMultisample != NULL)
703   {
704     theCtx->Functions()->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
705   }
706   else
707   {
708     theCtx->Functions()->glTexImage2DMultisample   (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
709   }
710 #else
711   theCtx->Functions()  ->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
712 #endif
713   if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
714   {
715     Unbind (theCtx);
716     return false;
717   }
718
719   mySizeX = theSizeX;
720   mySizeY = theSizeY;
721
722   Unbind (theCtx);
723   return true;
724 }
725
726 // =======================================================================
727 // function : InitRectangle
728 // purpose  :
729 // =======================================================================
730 bool OpenGl_Texture::InitRectangle (const Handle(OpenGl_Context)& theCtx,
731                                     const Standard_Integer        theSizeX,
732                                     const Standard_Integer        theSizeY,
733                                     const OpenGl_TextureFormat&   theFormat)
734 {
735   if (!Create (theCtx) || !theCtx->IsGlGreaterEqual (3, 0))
736   {
737     return false;
738   }
739
740 #if !defined(GL_ES_VERSION_2_0)
741   myTarget = GL_TEXTURE_RECTANGLE;
742   myNbSamples = 1;
743   myMaxMipLevel = 0;
744
745   const GLsizei aSizeX    = Min (theCtx->MaxTextureSize(), theSizeX);
746   const GLsizei aSizeY    = Min (theCtx->MaxTextureSize(), theSizeY);
747
748   Bind (theCtx);
749   applyDefaultSamplerParams (theCtx);
750
751   myTextFormat  = theFormat.Format();
752   mySizedFormat = theFormat.Internal();
753
754   // setup the alignment
755   OpenGl_UnpackAlignmentSentry::Reset();
756
757   glTexImage2D (GL_PROXY_TEXTURE_RECTANGLE, 0, mySizedFormat,
758                 aSizeX, aSizeY, 0,
759                 myTextFormat, GL_FLOAT, NULL);
760
761   GLint aTestSizeX = 0;
762   GLint aTestSizeY = 0;
763
764   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_WIDTH,  &aTestSizeX);
765   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_HEIGHT, &aTestSizeY);
766   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
767
768   if (aTestSizeX == 0 || aTestSizeY == 0)
769   {
770     Unbind (theCtx);
771     return false;
772   }
773
774   glTexImage2D (myTarget, 0, mySizedFormat,
775                 aSizeX, aSizeY, 0,
776                 myTextFormat, GL_FLOAT, NULL);
777
778   if (glGetError() != GL_NO_ERROR)
779   {
780     Unbind (theCtx);
781     return false;
782   }
783
784   mySizeX = aSizeX;
785   mySizeY = aSizeY;
786
787   Unbind (theCtx);
788   return true;
789 #else
790   (void )theSizeX;
791   (void )theSizeY;
792   (void )theFormat;
793   return false;
794 #endif
795 }
796
797 // =======================================================================
798 // function : Init3D
799 // purpose  :
800 // =======================================================================
801 bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
802                              const OpenGl_TextureFormat&   theFormat,
803                              const Graphic3d_Vec3i&        theSizeXYZ,
804                              const void*                   thePixels)
805 {
806   if (theCtx->Functions()->glTexImage3D == NULL)
807   {
808     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
809                          "Error: three-dimensional textures are not supported by hardware.");
810     return false;
811   }
812
813   if (!Create(theCtx))
814   {
815     return false;
816   }
817
818   myTarget = GL_TEXTURE_3D;
819   myNbSamples = 1;
820   myMaxMipLevel = 0;
821
822   const Graphic3d_Vec3i aSizeXYZ = theSizeXYZ.cwiseMin (Graphic3d_Vec3i (theCtx->MaxTextureSize()));
823   if (aSizeXYZ != theSizeXYZ)
824   {
825     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
826                          "Error: 3D texture dimensions exceed hardware limits.");
827     Release (theCtx.get());
828     Unbind (theCtx);
829     return false;
830   }
831   Bind (theCtx);
832
833   if (theFormat.DataType() == GL_FLOAT
834   && !theCtx->arbTexFloat)
835   {
836     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
837                          "Error: floating-point textures are not supported by hardware.");
838     Release (theCtx.get());
839     Unbind (theCtx);
840     return false;
841   }
842
843   mySizedFormat = theFormat.InternalFormat();
844
845   // setup the alignment
846   OpenGl_UnpackAlignmentSentry::Reset();
847
848 #if !defined (GL_ES_VERSION_2_0)
849   theCtx->core15fwd->glTexImage3D (GL_PROXY_TEXTURE_3D, 0, mySizedFormat,
850                                    aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
851                                    theFormat.PixelFormat(), theFormat.DataType(), NULL);
852
853   NCollection_Vec3<GLint> aTestSizeXYZ;
854   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH,  &aTestSizeXYZ.x());
855   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &aTestSizeXYZ.y());
856   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH,  &aTestSizeXYZ.z());
857   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
858   if (aTestSizeXYZ.x() == 0 || aTestSizeXYZ.y() == 0 || aTestSizeXYZ.z() == 0)
859   {
860     Unbind (theCtx);
861     Release (theCtx.get());
862     return false;
863   }
864 #endif
865
866   applyDefaultSamplerParams (theCtx);
867   theCtx->Functions()->glTexImage3D (myTarget, 0, mySizedFormat,
868                                      aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
869                                      theFormat.PixelFormat(), theFormat.DataType(), thePixels);
870
871   if (glGetError() != GL_NO_ERROR)
872   {
873     Unbind (theCtx);
874     Release (theCtx.get());
875     return false;
876   }
877
878   mySizeX = aSizeXYZ.x();
879   mySizeY = aSizeXYZ.y();
880   mySizeZ = aSizeXYZ.z();
881
882   Unbind (theCtx);
883   return true;
884 }
885
886 // =======================================================================
887 // function : InitCubeMap
888 // purpose  :
889 // =======================================================================
890 bool OpenGl_Texture::InitCubeMap (const Handle(OpenGl_Context)&    theCtx,
891                                   const Handle(Graphic3d_CubeMap)& theCubeMap,
892                                   Standard_Size    theSize,
893                                   Image_Format     theFormat,
894                                   Standard_Boolean theToGenMipmap,
895                                   Standard_Boolean theIsColorMap)
896 {
897   if (!Create (theCtx))
898   {
899     Release (theCtx.get());
900     return false;
901   }
902
903   Handle(Image_PixMap) anImage;
904   Handle(Image_CompressedPixMap) aCompImage;
905   OpenGl_TextureFormat aFormat;
906   if (!theCubeMap.IsNull())
907   {
908     theCubeMap->Reset();
909     if (theCtx->SupportedTextureFormats()->HasCompressed()
910     && !theCtx->caps->compressedTexturesDisable)
911     {
912       aCompImage = theCubeMap->CompressedValue (theCtx->SupportedTextureFormats());
913     }
914     if (!aCompImage.IsNull())
915     {
916       aFormat = OpenGl_TextureFormat::FindCompressedFormat (theCtx, aCompImage->CompressedFormat(), theIsColorMap);
917       if (aFormat.IsValid())
918       {
919         theToGenMipmap = false;
920         theSize   = aCompImage->SizeX();
921         theFormat = aCompImage->BaseFormat();
922         myMaxMipLevel = Max (aCompImage->MipMaps().Size() - 1, 0);
923         if (myMaxMipLevel > 0
924         && !aCompImage->IsCompleteMipMapSet())
925         {
926           const Graphic3d_Vec2i aMipSize = computeSmallestMipMapSize (Graphic3d_Vec2i (aCompImage->SizeX(), aCompImage->SizeY()), myMaxMipLevel);
927           if (!theCtx->HasTextureBaseLevel())
928           {
929             myMaxMipLevel = 0;
930             theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 0, GL_DEBUG_SEVERITY_MEDIUM,
931                                  TCollection_AsciiString ("Warning: Cubemap compressed texture ") + theCubeMap->GetId() + " " + aCompImage->SizeX() + "x" + aCompImage->SizeX()
932                                  + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y() + "; mipmaps will be ignored");
933           }
934           else
935           {
936             Message::SendTrace (TCollection_AsciiString ("Warning: Cubemap compressed texture ") + theCubeMap->GetId() + " " + aCompImage->SizeX() + "x" + aCompImage->SizeX()
937                                 + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y());
938           }
939         }
940
941         OpenGl_UnpackAlignmentSentry::Reset();
942       }
943       else
944       {
945         aCompImage.Nullify();
946       }
947     }
948
949     if (!aFormat.IsValid())
950     {
951       anImage = theCubeMap->Reset().Value (theCtx->SupportedTextureFormats());
952       if (anImage.IsNull())
953       {
954         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
955                              "Unable to get the first side of cubemap");
956         Release(theCtx.get());
957         return false;
958       }
959
960       theSize   = anImage->SizeX();
961       theFormat = anImage->Format();
962       theToGenMipmap = theCubeMap->HasMipmaps();
963       myMaxMipLevel = theToGenMipmap ? computeUpperMipMapLevel ((Standard_Integer )theSize) : 0;
964     }
965
966     myIsTopDown = theCubeMap->IsTopDown();
967   }
968   else
969   {
970     myMaxMipLevel = theToGenMipmap ? computeUpperMipMapLevel ((Standard_Integer )theSize) : 0;
971   }
972
973   if (!aFormat.IsValid())
974   {
975     aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theFormat, theIsColorMap);
976   }
977   if (!aFormat.IsValid())
978   {
979     Unbind(theCtx);
980     Release(theCtx.get());
981     return false;
982   }
983
984   myTarget = GL_TEXTURE_CUBE_MAP;
985   myNbSamples = 1;
986   mySizeX = (GLsizei )theSize;
987   mySizeY = (GLsizei )theSize;
988   myTextFormat  = aFormat.Format();
989   mySizedFormat = aFormat.Internal();
990 #if !defined(GL_ES_VERSION_2_0)
991   const GLint anIntFormat = aFormat.InternalFormat();
992 #else
993   // ES 2.0 does not support sized formats and format conversions - them detected from data type
994   const GLint anIntFormat = theCtx->IsGlGreaterEqual (3, 0) ? aFormat.InternalFormat() : aFormat.PixelFormat();
995 #endif
996
997   Bind (theCtx);
998   applyDefaultSamplerParams (theCtx);
999
1000   for (Standard_Integer i = 0; i < 6; ++i)
1001   {
1002     const Standard_Byte* aData = NULL;
1003
1004     if (!theCubeMap.IsNull())
1005     {
1006       if (i != 0)
1007       {
1008         if (!aCompImage.IsNull())
1009         {
1010           aCompImage = theCubeMap->CompressedValue (theCtx->SupportedTextureFormats());
1011         }
1012         else
1013         {
1014           anImage = theCubeMap->Value (theCtx->SupportedTextureFormats());
1015         }
1016       }
1017       if (!aCompImage.IsNull())
1018       {
1019         Graphic3d_Vec2i aMipSizeXY (mySizeX, mySizeY);
1020         aData = aCompImage->FaceData()->Data();
1021         for (Standard_Integer aMipIter = 0; aMipIter <= myMaxMipLevel; ++aMipIter)
1022         {
1023           const Standard_Integer aMipLength = aCompImage->MipMaps().Value (aMipIter);
1024           theCtx->Functions()->glCompressedTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, aMipIter, mySizedFormat, aMipSizeXY.x(), aMipSizeXY.y(), 0, aMipLength, aData);
1025           const GLenum aTexImgErr = glGetError();
1026           if (aTexImgErr != GL_NO_ERROR)
1027           {
1028             theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1029                                  TCollection_AsciiString ("Error: cubemap compressed texture ") + aMipSizeXY.x() + "x" + aMipSizeXY.y()
1030                                  + " IF: " + int(aFormat.Internal()) + " PF: " + int(aFormat.PixelFormat())
1031                                  + " DT: " + int(aFormat.DataType())
1032                                  + " can not be created with error " + int(aTexImgErr) + ".");
1033             Unbind (theCtx);
1034             Release (theCtx.get());
1035             return false;
1036           }
1037
1038           aData += aMipLength;
1039           aMipSizeXY /= 2;
1040           if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
1041           if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
1042         }
1043
1044         theCubeMap->Next();
1045         continue;
1046       }
1047
1048       if (!anImage.IsNull())
1049       {
1050 #if !defined(GL_ES_VERSION_2_0)
1051         const GLint anAligment = Min ((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
1052         glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
1053
1054         // notice that GL_UNPACK_ROW_LENGTH is not available on OpenGL ES 2.0 without GL_EXT_unpack_subimage extension
1055         const GLint anExtraBytes = GLint(anImage->RowExtraBytes());
1056         const GLint aPixelsWidth = GLint(anImage->SizeRowBytes() / anImage->SizePixelBytes());
1057         const GLint aRowLength = (anExtraBytes >= anAligment) ? aPixelsWidth : 0;
1058         glPixelStorei (GL_UNPACK_ROW_LENGTH, aRowLength);
1059 #else
1060         Handle(Image_PixMap) aCopyImage = new Image_PixMap();
1061         aCopyImage->InitTrash (theFormat, theSize, theSize);
1062         for (unsigned int y = 0; y < theSize; ++y)
1063         {
1064           for (unsigned int x = 0; x < theSize; ++x)
1065           {
1066             for (unsigned int aByte = 0; aByte < anImage->SizePixelBytes(); ++aByte)
1067             {
1068               aCopyImage->ChangeRawValue (y, x)[aByte] = anImage->RawValue (y, x)[aByte];
1069             }
1070           }
1071         }
1072         anImage = aCopyImage;
1073         const GLint anAligment = Min((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
1074         glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
1075 #endif
1076         aData = anImage->Data();
1077       }
1078       else
1079       {
1080         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1081                              TCollection_AsciiString() + "Unable to get [" + i + "] side of cubemap");
1082         Unbind (theCtx);
1083         Release (theCtx.get());
1084         return false;
1085       }
1086       theCubeMap->Next();
1087     }
1088
1089     glTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
1090                   anIntFormat,
1091                   GLsizei(theSize), GLsizei(theSize),
1092                   0, aFormat.PixelFormat(), aFormat.DataType(),
1093                   aData);
1094
1095     OpenGl_UnpackAlignmentSentry::Reset();
1096
1097     const GLenum anErr = glGetError();
1098     if (anErr != GL_NO_ERROR)
1099     {
1100       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1101                            TCollection_AsciiString ("Unable to initialize side of cubemap. Error #") + int(anErr));
1102       Unbind (theCtx);
1103       Release (theCtx.get());
1104       return false;
1105     }
1106   }
1107
1108   if (theToGenMipmap && theCtx->arbFBO != NULL)
1109   {
1110     theCtx->arbFBO->glGenerateMipmap (myTarget);
1111     const GLenum anErr = glGetError();
1112     if (anErr != GL_NO_ERROR)
1113     {
1114       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1115                            TCollection_AsciiString ("Unable to generate mipmap of cubemap. Error #") + int(anErr));
1116       Unbind (theCtx);
1117       Release (theCtx.get());
1118       return false;
1119     }
1120   }
1121
1122   Unbind (theCtx.get());
1123   return true;
1124 }
1125
1126 // =======================================================================
1127 // function : PixelSizeOfPixelFormat
1128 // purpose  :
1129 // =======================================================================
1130 Standard_Size OpenGl_Texture::PixelSizeOfPixelFormat (Standard_Integer theInternalFormat)
1131 {
1132   switch(theInternalFormat)
1133   {
1134     // RED variations (GL_RED, OpenGL 3.0+)
1135     case GL_RED:
1136     case GL_R8:       return 1;
1137     case GL_R16:      return 2;
1138     case GL_R16F:     return 2;
1139     case GL_R32F:     return 4;
1140     // RGB variations
1141     case GL_RGB:      return 3;
1142     case GL_RGB8:     return 3;
1143     case GL_RGB16:    return 6;
1144     case GL_RGB16F:   return 6;
1145     case GL_RGB32F:   return 12;
1146     // RGBA variations
1147     case GL_RGBA:     return 4;
1148     case GL_RGBA8:    return 4;
1149     case GL_RGB10_A2: return 4;
1150     case GL_RGBA12:   return 6;
1151     case GL_RGBA16:   return 8;
1152     case GL_RGBA16F:  return 8;
1153     case GL_RGBA32F:  return 16;
1154     //
1155     case GL_BGRA_EXT:  return 4;
1156     // ALPHA variations (deprecated)
1157     case GL_ALPHA:
1158     case GL_ALPHA8:    return 1;
1159     case GL_ALPHA16:   return 2;
1160     case GL_LUMINANCE: return 1;
1161     case GL_LUMINANCE_ALPHA: return 2;
1162     // depth-stencil
1163     case GL_DEPTH24_STENCIL8:   return 4;
1164     case GL_DEPTH32F_STENCIL8:  return 8;
1165     case GL_DEPTH_COMPONENT16:  return 2;
1166     case GL_DEPTH_COMPONENT24:  return 3;
1167     case GL_DEPTH_COMPONENT32F: return 4;
1168     // compressed
1169     case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:  // DXT1 uses circa half a byte per pixel (64 bits per 4x4 block)
1170     case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
1171     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1172     case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
1173     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: // DXT3/5 uses circa 1 byte per pixel (128 bits per 4x4 block)
1174     case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
1175     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1176     case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
1177       return 1;
1178   }
1179   return 1;
1180 }
1181
1182 // =======================================================================
1183 // function : EstimatedDataSize
1184 // purpose  :
1185 // =======================================================================
1186 Standard_Size OpenGl_Texture::EstimatedDataSize() const
1187 {
1188   if (!IsValid())
1189   {
1190     return 0;
1191   }
1192
1193   Standard_Size aSize = PixelSizeOfPixelFormat (mySizedFormat) * mySizeX * myNbSamples;
1194   if (mySizeY != 0)
1195   {
1196     aSize *= Standard_Size(mySizeY);
1197   }
1198   if (mySizeZ != 0)
1199   {
1200     aSize *= Standard_Size(mySizeZ);
1201   }
1202   if (myTarget == GL_TEXTURE_CUBE_MAP)
1203   {
1204     aSize *= 6; // cube sides
1205   }
1206   if (myMaxMipLevel > 0)
1207   {
1208     aSize = aSize + aSize / 3;
1209   }
1210   return aSize;
1211 }