0029138: Visualization - D3DHost_FrameBuffer should provide software fallback when...
[occt.git] / src / OpenGl / OpenGl_FrameBuffer.cxx
index f58dd92..4be39d9 100644 (file)
@@ -15,6 +15,7 @@
 #include <OpenGl_FrameBuffer.hxx>
 #include <OpenGl_ArbFBO.hxx>
 
+#include <NCollection_AlignedAllocator.hxx>
 #include <Standard_Assert.hxx>
 #include <TCollection_ExtendedString.hxx>
 
@@ -818,3 +819,240 @@ void OpenGl_FrameBuffer::UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx)
     theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, NO_FRAMEBUFFER);
   }
 }
+
+// =======================================================================
+// function : getAligned
+// purpose  :
+// =======================================================================
+inline Standard_Size getAligned (const Standard_Size theNumber,
+                                 const Standard_Size theAlignment)
+{
+  return theNumber + theAlignment - 1 - (theNumber - 1) % theAlignment;
+}
+
+template<typename T>
+inline void convertRowFromRgba (T* theRgbRow,
+                                const Image_ColorRGBA* theRgbaRow,
+                                const Standard_Size theWidth)
+{
+  for (Standard_Size aCol = 0; aCol < theWidth; ++aCol)
+  {
+    const Image_ColorRGBA& anRgba = theRgbaRow[aCol];
+    T& anRgb = theRgbRow[aCol];
+    anRgb.r() = anRgba.r();
+    anRgb.g() = anRgba.g();
+    anRgb.b() = anRgba.b();
+  }
+}
+
+// =======================================================================
+// function : BufferDump
+// purpose  :
+// =======================================================================
+Standard_Boolean OpenGl_FrameBuffer::BufferDump (const Handle(OpenGl_Context)& theGlCtx,
+                                                 const Handle(OpenGl_FrameBuffer)& theFbo,
+                                                 Image_PixMap& theImage,
+                                                 Graphic3d_BufferType theBufferType)
+{
+  if (theGlCtx.IsNull()
+   || theImage.IsEmpty())
+  {
+    return Standard_False;
+  }
+
+  GLenum aFormat = 0;
+  GLenum aType   = 0;
+  bool toSwapRgbaBgra = false;
+  bool toConvRgba2Rgb = false;
+  switch (theImage.Format())
+  {
+  #if !defined(GL_ES_VERSION_2_0)
+    case Image_Format_Gray:
+      aFormat = GL_DEPTH_COMPONENT;
+      aType   = GL_UNSIGNED_BYTE;
+      break;
+    case Image_Format_GrayF:
+      aFormat = GL_DEPTH_COMPONENT;
+      aType   = GL_FLOAT;
+      break;
+    case Image_Format_RGB:
+      aFormat = GL_RGB;
+      aType   = GL_UNSIGNED_BYTE;
+      break;
+    case Image_Format_BGR:
+      aFormat = GL_BGR;
+      aType   = GL_UNSIGNED_BYTE;
+      break;
+    case Image_Format_BGRA:
+    case Image_Format_BGR32:
+      aFormat = GL_BGRA;
+      aType   = GL_UNSIGNED_BYTE;
+      break;
+    case Image_Format_BGRF:
+      aFormat = GL_BGR;
+      aType   = GL_FLOAT;
+      break;
+    case Image_Format_BGRAF:
+      aFormat = GL_BGRA;
+      aType   = GL_FLOAT;
+      break;
+  #else
+    case Image_Format_Gray:
+    case Image_Format_GrayF:
+    case Image_Format_BGRF:
+    case Image_Format_BGRAF:
+      return Standard_False;
+    case Image_Format_BGRA:
+    case Image_Format_BGR32:
+      aFormat = GL_RGBA;
+      aType   = GL_UNSIGNED_BYTE;
+      toSwapRgbaBgra = true;
+      break;
+    case Image_Format_BGR:
+    case Image_Format_RGB:
+      aFormat = GL_RGBA;
+      aType   = GL_UNSIGNED_BYTE;
+      toConvRgba2Rgb = true;
+      break;
+  #endif
+    case Image_Format_RGBA:
+    case Image_Format_RGB32:
+      aFormat = GL_RGBA;
+      aType   = GL_UNSIGNED_BYTE;
+      break;
+    case Image_Format_RGBF:
+      aFormat = GL_RGB;
+      aType   = GL_FLOAT;
+      break;
+    case Image_Format_RGBAF:
+      aFormat = GL_RGBA;
+      aType   = GL_FLOAT;
+      break;
+    case Image_Format_Alpha:
+    case Image_Format_AlphaF:
+      return Standard_False; // GL_ALPHA is no more supported in core context
+    case Image_Format_UNKNOWN:
+      return Standard_False;
+  }
+
+  if (aFormat == 0)
+  {
+    return Standard_False;
+  }
+
+#if !defined(GL_ES_VERSION_2_0)
+  GLint aReadBufferPrev = GL_BACK;
+  if (theBufferType == Graphic3d_BT_Depth
+   && aFormat != GL_DEPTH_COMPONENT)
+  {
+    return Standard_False;
+  }
+#else
+  (void )theBufferType;
+#endif
+
+  // bind FBO if used
+  if (!theFbo.IsNull() && theFbo->IsValid())
+  {
+    theFbo->BindBuffer (theGlCtx);
+  }
+  else
+  {
+  #if !defined(GL_ES_VERSION_2_0)
+    glGetIntegerv (GL_READ_BUFFER, &aReadBufferPrev);
+    GLint aDrawBufferPrev = GL_BACK;
+    glGetIntegerv (GL_DRAW_BUFFER, &aDrawBufferPrev);
+    glReadBuffer (aDrawBufferPrev);
+  #endif
+  }
+
+  // setup alignment
+  const GLint anAligment   = Min (GLint(theImage.MaxRowAligmentBytes()), 8); // limit to 8 bytes for OpenGL
+  glPixelStorei (GL_PACK_ALIGNMENT, anAligment);
+  bool isBatchCopy = !theImage.IsTopDown();
+
+  const GLint   anExtraBytes       = GLint(theImage.RowExtraBytes());
+  GLint         aPixelsWidth       = GLint(theImage.SizeRowBytes() / theImage.SizePixelBytes());
+  Standard_Size aSizeRowBytesEstim = getAligned (theImage.SizePixelBytes() * aPixelsWidth, anAligment);
+  if (anExtraBytes < anAligment)
+  {
+    aPixelsWidth = 0;
+  }
+  else if (aSizeRowBytesEstim != theImage.SizeRowBytes())
+  {
+    aPixelsWidth = 0;
+    isBatchCopy  = false;
+  }
+#if !defined(GL_ES_VERSION_2_0)
+  glPixelStorei (GL_PACK_ROW_LENGTH, aPixelsWidth);
+#else
+  if (aPixelsWidth != 0)
+  {
+    isBatchCopy = false;
+  }
+#endif
+  if (toConvRgba2Rgb)
+  {
+    Handle(NCollection_BaseAllocator) anAlloc = new NCollection_AlignedAllocator (16);
+    const Standard_Size aRowSize = theImage.SizeX() * 4;
+    NCollection_Buffer aRowBuffer (anAlloc);
+    if (!aRowBuffer.Allocate (aRowSize))
+    {
+      return Standard_False;
+    }
+
+    for (Standard_Size aRow = 0; aRow < theImage.SizeY(); ++aRow)
+    {
+      // Image_PixMap rows indexation always starts from the upper corner
+      // while order in memory depends on the flag and processed by ChangeRow() method
+      glReadPixels (0, GLint(theImage.SizeY() - aRow - 1), GLsizei (theImage.SizeX()), 1, aFormat, aType, aRowBuffer.ChangeData());
+      const Image_ColorRGBA* aRowDataRgba = (const Image_ColorRGBA* )aRowBuffer.Data();
+      if (theImage.Format() == Image_Format_BGR)
+      {
+        convertRowFromRgba ((Image_ColorBGR* )theImage.ChangeRow (aRow), aRowDataRgba, theImage.SizeX());
+      }
+      else
+      {
+        convertRowFromRgba ((Image_ColorRGB* )theImage.ChangeRow (aRow), aRowDataRgba, theImage.SizeX());
+      }
+    }
+  }
+  else if (!isBatchCopy)
+  {
+    // copy row by row
+    for (Standard_Size aRow = 0; aRow < theImage.SizeY(); ++aRow)
+    {
+      // Image_PixMap rows indexation always starts from the upper corner
+      // while order in memory depends on the flag and processed by ChangeRow() method
+      glReadPixels (0, GLint(theImage.SizeY() - aRow - 1), GLsizei (theImage.SizeX()), 1, aFormat, aType, theImage.ChangeRow (aRow));
+    }
+  }
+  else
+  {
+    glReadPixels (0, 0, GLsizei (theImage.SizeX()), GLsizei (theImage.SizeY()), aFormat, aType, theImage.ChangeData());
+  }
+  const bool hasErrors = theGlCtx->ResetErrors (true);
+
+  glPixelStorei (GL_PACK_ALIGNMENT,  1);
+#if !defined(GL_ES_VERSION_2_0)
+  glPixelStorei (GL_PACK_ROW_LENGTH, 0);
+#endif
+
+  if (!theFbo.IsNull() && theFbo->IsValid())
+  {
+    theFbo->UnbindBuffer (theGlCtx);
+  }
+  else
+  {
+  #if !defined(GL_ES_VERSION_2_0)
+    glReadBuffer (aReadBufferPrev);
+  #endif
+  }
+
+  if (toSwapRgbaBgra)
+  {
+    Image_PixMap::SwapRgbaBgra (theImage);
+  }
+
+  return !hasErrors;
+}